diff --git a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-classes-methods.md b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-classes-methods.md index b4f6cca84ed..295725db0f4 100644 --- a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-classes-methods.md +++ b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-classes-methods.md @@ -21,13 +21,13 @@ A *class* is a software construct that defines the data and methods of the insta *Variables* contain the data for the class. Every instance that is constructed from the class declaration has its own copy of the variables. These variables are known as *instance variables*. -Methods define the behavior of a class. They are the sequences of statements that operate on the data (instance variables). By default, methods are declared to operate on the instance variables of the class. These methods are known as *instance methods* or *object methods*. +Methods define the behavior of a class. They are the sequences of statements that operate on the data (instance variables). By default, methods are declared to operate on the instance variables of the class. These methods are known as *instance methods* or *object methods*. You can declare *static methods* and *static fields*, that do not have access to *instance variables*. These are described in [X++ static classes](xpp-static-classes.md). ## Declare a class -You must use the **Add new item** dialog in Visual Studio to add a class to your project. +You must use the **Add new item** dialog in Visual Studio to add a class to your project. 1. In Server Explorer, right-click the project, and then click **Add**. 2. In the **New Item** dialog box, select **Installed > Dynamics 365 Items > Code** in the left navigation. Then select **Class**, and then enter a name for the class. @@ -77,19 +77,19 @@ public static void TestLastName() ## Constructors -To create an instance of a class, you must instantiate it by using a *constructor*. +To create an instance of a class, you must instantiate it by using a *constructor*. -+ You can define only one **new** method (constructor) in a class. -+ If you do not define a constructor, a default constructor with no parameters is created automatically by the compiler. ++ You can define only one **new** method (constructor) in a class. ++ If you do not define a constructor, a default constructor with no parameters is created automatically by the compiler. + You can simulate a default constructor by assigning default values to the parameters in the **new** method. -The following examples defines a parameterless constructor in the **Point** class. +The following example defines a parameterless constructor in the **Point** class. ```xpp class Point { - // Instance variables that are public. In practice, you would probably make this protected or private + // Instance variables that are public. In practice, you would probably make this protected or private // and create accessor methods. public real x = 0.0; public real y = 0.0; @@ -102,7 +102,7 @@ class Point Following is information about how to create a clean inheritance model and minimize problems when code is upgraded: + Each class must have a single public construction method unless the class is abstract. If no initialization is required, use a static construct method. Otherwise, use a static **new** method (the default constructor for the class should be protected). -+ Each class should have at least one static **construct** method method. ++ Each class should have at least one static **construct** method. + Each class should have at least one static **new** method. + Each class should have a **new** method (the default constructor). This method should be **protected**. + Create accessor methods to get and set class variables. @@ -170,6 +170,7 @@ Point ap = new Point(); ``` ## Destructors + You use a *destructor* to explicitly destroy a class instance. Instances are automatically destroyed when there are no references to them. However, you can destroy objects explicitly in the following ways: + Use the **finalize** method. @@ -186,13 +187,13 @@ The following example shows the basic structure for a call to the **finalize** m if (condition) { // Removes object from memory. - this.finalize(); + this.finalize(); } ``` ### Set reference variable to null -Set the reference variable to **null** to terminate an object. This approach destroys an object only if no other variables point to that object. You should verify that other code isn't using the variable. The following example creates an reference variable and then sets it to **null**. +Set the reference variable to **null** to terminate an object. This approach destroys an object only if no other variables point to that object. You should verify that other code isn't using the variable. The following example creates a reference variable and then sets it to **null**. ```xpp Point myPoint = new Point(); @@ -230,7 +231,7 @@ info(int2Str(area)); ### Static methods -Static methods, which are also known as *class methods*, belong to a class and are created by using the keyword **static**. You don't have to instantiate an object before you use static methods. Static methods are often used to work with data that is stored in tables. Member variables can't be used in a static method. +Static methods, which are also known as *class methods*, belong to a class and are created by using the keyword **static**. You don't have to instantiate an object before you use static methods. Static methods are often used to work with data that is stored in tables. Member variables can't be used in a static method. You use the following syntax to call static methods. @@ -257,7 +258,7 @@ Method declarations consist of a header and a body. The method header declares t ### Return type -A return type is required for each method. If a method doesn't return anything, use the **void** keyword as the return type. +A return type is required for each method. If a method doesn't return anything, use the **void** keyword as the return type. The following example shows two methods. One method has a return type, but the other method doesn't have a return type. @@ -276,21 +277,21 @@ int methodNameIntegerReturnValue() ### Syntax -Method declaration = *Heading* *Body* Heading = **\[** *Modifiers* **\]** *ReturnType* *MethodName* **(** *ParameterList* **)** +Method declaration = *Heading* *Body* Heading = **\[** *Modifiers* **\]** *ReturnType* *MethodName* **(** *ParameterList* **)** -Modifiers = **\[client\] \[server\] \[edit | display | public | protected | private\] \[static | abstract | final \]** +Modifiers = **\[client\] \[server\] \[edit | display | public | protected | private\] \[static | abstract | final \]** -ReturnType = *Datatype* **| void | anytype** +ReturnType = *Datatype* **| void | anytype** -MethodName = *Identifier* +MethodName = *Identifier* -ParameterList = **\[** *Parameter* **{ ,** *Parameter* **}\]** +ParameterList = **\[** *Parameter* **{ ,** *Parameter* **}\]** -Parameter = *Datatype* *Variableidentifier* **\[ =** *Expression* **\]** +Parameter = *Datatype* *Variableidentifier* **\[ =** *Expression* **\]** -Body = **{ \[** *VariableDeclarations* **\] \[** *EmbeddedFunctionDeclarations* **\] \[** *Statements* **\] }** +Body = **{ \[** *VariableDeclarations* **\] \[** *EmbeddedFunctionDeclarations* **\] \[** *Statements* **\] }** -EmbeddedFunctionDeclaration = *Heading* **{\[** *VariableDeclarations* **\] \[** *Statements* **\]}** +EmbeddedFunctionDeclaration = *Heading* **{\[** *VariableDeclarations* **\] \[** *Statements* **\]}** If you use the **anytype** return type, the method can return any data type. @@ -298,7 +299,7 @@ If you use the **anytype** return type, the method can return any data type. ```xpp void update () -{ +{ // Variable declared and initialized CustTable this_Orig = this.orig(); @@ -325,8 +326,8 @@ In the following example, the **checkAccountBlocked** method returns a Boolean v ```xpp boolean checkAccountBlocked(AmountCur amountCur) { - if (this.blocked == CustVendorBlocked::All - ||(this.blocked == CustVendorBlocked::Invoice + if (this.blocked == CustVendorBlocked::All + ||(this.blocked == CustVendorBlocked::Invoice && amountCur > 0 )) return checkFailed(strFmt("@SYS7987",this.accountNum)); return true; @@ -334,11 +335,12 @@ boolean checkAccountBlocked(AmountCur amountCur) ``` ## Method modifiers + Several modifiers can be applied to method declarations. Some of the modifiers can be combined (for example, **final static**). Here are the method modifier keywords: -+ **abstract**: The method is declared but isn't implemented in a parent class. The method must be overridden in subclasses. If you try to create an object from a subclass where one or more abstract methods that belong to the parent class haven't been overridden, you receive a compiler error. ++ **abstract**: The method is declared but isn't implemented in a parent class. The method must be overridden in subclasses. If you try to create an object from a subclass where one or more abstract methods that belong to the parent class haven't been overridden, you receive a compiler error. - Classes can also be abstract. Sometimes, a class should not be instantiated even though it represents an abstract concept. Only subclasses should be instantiated. Base classes of this type can be declared as **abstract**. For example, you want to model the concept of an account. Accounts are abstract, because only derived classes (ledger accounts and so on) exist in the real world. This examples describes a clear case where you should declare the **Account** class as **abstract**. + Classes can also be abstract. Sometimes, a class should not be instantiated even though it represents an abstract concept. Only subclasses should be instantiated. Base classes of this type can be declared as **abstract**. For example, you want to model the concept of an account. Accounts are abstract, because only derived classes (ledger accounts and so on) exist in the real world. This example describes a clear case where you should declare the **Account** class as **abstract**. + **display**: The method's return value should be shown on a page or a report. The value can't be modified on the page or report. Typically, the return value is a calculated value, such as a sum. + **edit**: The method's return type should be used to provide information for a field that is used on a page. The value in the field can be modified. + **final**: The method can't be overridden in any class that derives from its class. @@ -353,9 +355,9 @@ The following examples show only the method headers. ```xpp // A method that cannot be overridden -final int dontAlterMe() +final int dontAlterMe() -// A static method +// A static method static void noChange() // A display method that returns an integer @@ -398,7 +400,7 @@ class Person { date birthDate; - // The constructor that takes a date type as a parameter. + // The constructor that takes a date type as a parameter. // That value is assigned to the field member birthDate. void new(date _date) { @@ -419,7 +421,7 @@ class Person Person person = new Person(13\5\2010); // Optional parameter's default is used. - Info(strFmt('Age in years today is %1 years', + Info(strFmt('Age in years today is %1 years', real2int(person.CalculateAgeAsOfDate()))); // January 2, 2044 is the parameter value for _date. @@ -431,7 +433,7 @@ class Person } ``` -This is an example of how you cannot skip to a second optional parameter. The **AddThreeInts** method has two optional parameters. The **callAdditions** method calls the **AddThreeInts** method. The commented out code tries to override only the **\_i3** default value, but the compiler requires that all prior optional parameters also be overridden in the call. +This is an example of how you cannot skip to a second optional parameter. The **AddThreeInts** method has two optional parameters. The **callAdditions** method calls the **AddThreeInts** method. The commented out code tries to override only the **\_i3** default value, but the compiler requires that all prior optional parameters also be overridden in the call. ```xpp class Additions @@ -461,33 +463,33 @@ Class variables are protected by default. By hiding details of the internal impl class Point { // Instance variables - real x; + real x; real y; // Constructor to initialize to a specific or default value - void new(real _x = 10, real _y = 10) + void new(real _x = 10, real _y = 10) { x = _x; y = _y; } // Accessor methods - void setX(real _x) + void setX(real _x) { x = _x; } - void setY(real _y) + void setY(real _y) { y = _y; } - real getX() + real getX() { return x; } - real getY() + real getY() { return y; } @@ -514,7 +516,7 @@ A scope defines the area in which an item can be accessed. Variables that are de ## Local functions -You can declare functions inside a method. These are called local functions. While possible, it is not a best practice. Instead, you should add private methods to the class. +You can declare functions inside a method. These are called local functions. While possible, it is not a best practice. Instead, you should add private methods to the class. + The declarations of local functions must physically precede any non-declaration statements in the method. + You can declare more than one local function in your method. However, all local functions must be declared in an uninterrupted series, and the set must be terminated by one semicolon (;). @@ -626,4 +628,4 @@ There is no concept of an X++ job from preview versions (AX2102 and earlier). To The depth of the call stack is limited to 100. -[!INCLUDE[footer-include](../../../includes/footer-banner.md)] \ No newline at end of file +[!INCLUDE[footer-include](../../../includes/footer-banner.md)] diff --git a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-events.md b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-events.md index c73d43e8bed..684b42d6c76 100644 --- a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-events.md +++ b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-events.md @@ -43,7 +43,7 @@ The following table shows the keywords that describe the use of delegates. ## Example -The two classes in the following code example demonstrate how to define an event, subscribe to an event, and raise an event. The **PointWithEvent** class defines a delegate, **moved**. The **move** method calls the **moved** delegate, thereby notifing any objects that have subscribed to the event. The **PointKeeper** class defines the **writeMove** method and assigns it as the event handler for the **moved** delegate of the **Point** instance created in the **createAndMove** method. +The two classes in the following code example demonstrate how to define an event, subscribe to an event, and raise an event. The **PointWithEvent** class defines a delegate, **moved**. The **move** method calls the **moved** delegate, thereby notifying any objects that have subscribed to the event. The **PointKeeper** class defines the **writeMove** method and assigns it as the event handler for the **moved** delegate of the **Point** instance created in the **createAndMove** method. ```xpp class PointWithEvent diff --git a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-exceptions.md b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-exceptions.md index 425bf711305..1e47a89d58f 100644 --- a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-exceptions.md +++ b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-exceptions.md @@ -17,7 +17,7 @@ ms.dyn365.ops.version: AX 7.0.0 This topic describes exception handling in X++. You handle errors by using the **throw**, **try**...**catch**, **finally**, and **retry** statements to generate and handle exceptions. -An *exception* is a regulated jump away from the sequence of program execution. The instruction where program execution resumes is determined by **try**...**catch** blocks and the type of exception that is thrown. An exception is represented by a value of the **Exception** enumeration, or an instance of .NET's System.Exception class or a derived class. One exception that is often thrown is the **Exception::error** enum value. A common practice is to write diagnostic information to the Infolog before the exception is thrown. +An *exception* is a regulated jump away from the sequence of program execution. The instruction where program execution resumes is determined by `try...catch` blocks and the type of exception that is thrown. An exception is represented by a value of the **Exception** enumeration, or an instance of .NET's `System.Exception` class or a derived class. One exception that is often thrown is the **Exception::error** enum value. A common practice is to write diagnostic information to the Infolog before the exception is thrown. The **Global::error** method is often the best way to write diagnostic information to the Infolog. For example, your method might receive an input parameter value that isn't valid. In this case, the method can throw an exception to immediately transfer control to a **catch** code block that contains logic for handling this error situation. You don't necessarily have to know the location of the **catch** block that will receive control when the exception is thrown. @@ -514,6 +514,6 @@ The following table shows the exception literals that are the values of the **Ex | UpdateConflict | An error occurred in a transaction that is using Optimistic Concurrency Control. The transaction can be retried (use a **retry** statement in the **catch** block). | | UpdateConflictNotRecovered | An error occurred in a transaction that is using Optimistic Concurrency Control. The code won't be retried. This exception can't be caught within a transaction. | | Warning | An exceptional event has occurred. Although the user might have to take action, the event isn't fatal. Don't throw a **warning** exception. | -| [SQL connection error X++ exception](sql-connection-error.md) | An error occured when during the query execution. The transaction will be canceled. This exception can't be caught within a transaction. | +| [SQL connection error X++ exception](sql-connection-error.md) | An error occurred when during the query execution. The transaction will be canceled. This exception can't be caught within a transaction. | [!INCLUDE[footer-include](../../../includes/footer-banner.md)] diff --git a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-language-reference.md b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-language-reference.md index 122b98ff67c..1b58a85422b 100644 --- a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-language-reference.md +++ b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-language-reference.md @@ -26,7 +26,7 @@ X++ is an object-oriented, application-aware, and data-aware programming languag | Best practice checks | X++ code is checked for syntax errors during compile time. The compile process also performs best practice checks. Violations of best practices can generate compiler messages.| | Garbage collection | The X++ runtime execution engines have automatic mechanisms to discard objects that are no longer referenced, so that memory space can be reused. | | Interoperability | Interoperability between classes written in X++ and in C\# (or other .NET Framework languages) is supported. | -| File manipulation | File input and output is supported, including XML building and parsing. | +| File manipulation | File input and output are supported, including XML building and parsing. | | Collections | Dynamic arrays are supported and the X++ includes several collection objects.| ## X++ compiles to Microsoft .NET CIL (Common Intermediate Language) diff --git a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-macros.md b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-macros.md index 1282b3deab8..ba810c49dcf 100644 --- a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-macros.md +++ b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-macros.md @@ -71,7 +71,7 @@ You can define macro values to include parameter symbols. The first parameter sy ## \#localmacro and \#globalmacro directives -The `#localmacro` directive is a good choice when you want a macro to have a value that is several lines long, or when your macro value contains a closing parenthesis. The `#localmacro` directive is a good choice when you want your macro value to be lines of X++ or SQL code. The `#localmacro` directive can be written as `#macro.` However, `#localmacro` is the recommended term. Both macros have the same behavior. By using the `#if` directive, you can test whether a macro name is declared with the `#define` directive. However, you cannot test whether the macro name is declared with the `#localmacro` directive. Only macros declared by using the `#define` directive are affected by the `#undef` directive. In a `#define` directive, you can specify a name that is already in scope as a `#localmacro.` The effect is to discard the `#localmacro` and create a `#define` macro. This also applies to the opposite sequence, which means that a `#localmacro` can redefine a `#define.` A `#localmacro` (that has both a macro name and a value) always overrides a previous `#localmacro` that has the same name. However, you cannot always be sure whether the override occurs when you use `#globalmacro.` For this reason we recommend that you do not use `#globalmacro`. This same problem occurs with `#globaldefine.` The main difference between a `#define` macro and a `#localmacro` macro is in how their syntax is terminated. The terminators are as follows: +The `#localmacro` directive is a good choice when you want a macro to have a value that is several lines long, or when your macro value contains a closing parenthesis. The `#localmacro` directive is a good choice when you want your macro value to be lines of X++ or SQL code. The `#localmacro` directive can be written as `#macro`. However, `#localmacro` is the recommended term. Both macros have the same behavior. By using the `#if` directive, you can test whether a macro name is declared with the `#define` directive. However, you cannot test whether the macro name is declared with the `#localmacro` directive. Only macros declared by using the `#define` directive are affected by the `#undef` directive. In a `#define` directive, you can specify a name that is already in scope as a `#localmacro`. The effect is to discard the `#localmacro` and create a `#define` macro. This also applies to the opposite sequence, which means that a `#localmacro` can redefine a `#define`. A `#localmacro` (that has both a macro name and a value) always overrides a previous `#localmacro` that has the same name. However, you cannot always be sure whether the override occurs when you use `#globalmacro.` For this reason we recommend that you do not use `#globalmacro`. This same problem occurs with `#globaldefine`. The main difference between a `#define` macro and a `#localmacro` macro is in how their syntax is terminated. The terminators are as follows: - `#define` – is terminated by– `)` - `#localmacro` – is terminated by– `#endmacro` @@ -94,10 +94,10 @@ You can use the `#linenumber` directive during your development and debugging of ## Range (scope) of macros -The range in which a macro can be referenced depends on where the macro is defined. In a class, macros that are defined in the parent class can be referenced, but macros defined in a child class cannot be referenced. When the precompiler handles a child class, the precompiler first traces the inheritance chain to the most ascendant class. The precompiler processes all the directives from the class declaration part of the ascendant class. It stores all the macros and their values in its internal tables. The precompiler handles the next class in the inheritance chain the same way. The result of the directives in each class declaration are applied to the internal tables that are already populated from directives that were found earlier in the inheritance chain. When the precompiler reaches the target child class, it again handles the class declaration part. However, it next handles each method in a series of separate operations. The precompiler updates its internal tables in a way that the state of the tables can be restored as they were before processing of the current method began. After the first method is handled, the internal tables are restored before the next method is handled. +The range in which a macro can be referenced depends on where the macro is defined. In a class, macros that are defined in the parent class can be referenced, but macros defined in a child class cannot be referenced. When the precompiler handles a child class, the precompiler first traces the inheritance chain to the most ascendant class. The precompiler processes all the directives from the class declaration part of the ascendant class. It stores all the macros and their values in its internal tables. The precompiler handles the next class in the inheritance chain the same way. The results of the directives in each class declaration are applied to the internal tables that are already populated from directives that were found earlier in the inheritance chain. When the precompiler reaches the target child class, it again handles the class declaration part. However, it next handles each method in a series of separate operations. The precompiler updates its internal tables in a way that the state of the tables can be restored as they were before processing of the current method began. After the first method is handled, the internal tables are restored before the next method is handled. ### The Method is All Contents of the Node In this context, a method is defined as the contents of a method node in the Application Object Tree (AOT). In the AOT, you can expand the Classes node, expand a class node, right-click a method node, and then select Edit. Then you can add a line for `#define.MyMacro("abc")` before the method declaration. The precompiler treats this `#define` directive as part of the method, even though the `#define` occurs outside the `{}` block of the method. -[!INCLUDE[footer-include](../../../includes/footer-banner.md)] \ No newline at end of file +[!INCLUDE[footer-include](../../../includes/footer-banner.md)] diff --git a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-statements-loops.md b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-statements-loops.md index 74a794ce883..6a3b0b5b715 100644 --- a/articles/fin-ops-core/dev-itpro/dev-ref/xpp-statements-loops.md +++ b/articles/fin-ops-core/dev-itpro/dev-ref/xpp-statements-loops.md @@ -34,7 +34,7 @@ You use the **print** statement to output text through **System.Diagnostics.Writ | Feature | print statement | info method | |-----------|--------------------|--------------| | Ease of invocation | The **print** statement automatically converts various data types into strings. It can convert multiple data types in one invocation. | The **info** method requires that the input parameter be a string. | -| Ability to copy contents to the clipboard | Text is easily copied from the **Output** window to the clipboard. | Text is easily easily copied from the **Infolog** window to the clipboard. | +| Ability to copy contents to the clipboard | Text is easily copied from the **Output** window to the clipboard. | Text is easily copied from the **Infolog** window to the clipboard. | | Typical usage | The **print** statement is used for convenience during testing. It can help you debug small issues without having to run a formal debugger. | The **info** method is appropriate for use in production. | ### Example of a print statement diff --git a/articles/fin-ops-core/dev-itpro/dev-tools/debug-xpp.md b/articles/fin-ops-core/dev-itpro/dev-tools/debug-xpp.md index fbcbc612509..c76dac0ef18 100644 --- a/articles/fin-ops-core/dev-itpro/dev-tools/debug-xpp.md +++ b/articles/fin-ops-core/dev-itpro/dev-tools/debug-xpp.md @@ -123,6 +123,6 @@ Put a breakpoint on the print statements by pressing F9 while that statement is ## The Immediate window -The **Immediate** window is a debugger feature that lets you to enter expressions and statements to evaluate at any given time. This feature isn't implemented in the X++ stack. However, you can still benefit from the immediate window. The snippets must be expressed in C\#, not in X++. +The **Immediate** window is a debugger feature that lets you enter expressions and statements to evaluate at any given time. This feature isn't implemented in the X++ stack. However, you can still benefit from the immediate window. The snippets must be expressed in C\#, not in X++. [!INCLUDE[footer-include](../../../includes/footer-banner.md)] diff --git a/articles/fin-ops-core/dev-itpro/extensibility/customize-model-elements-extensions.md b/articles/fin-ops-core/dev-itpro/extensibility/customize-model-elements-extensions.md index 1d88d310ef6..89bb0c1ec8e 100644 --- a/articles/fin-ops-core/dev-itpro/extensibility/customize-model-elements-extensions.md +++ b/articles/fin-ops-core/dev-itpro/extensibility/customize-model-elements-extensions.md @@ -465,5 +465,4 @@ To deploy your extension to another environment, for example, a test, pre-produc [Download FMLab sample code](https://github.com/Microsoft/FMLab) - -[!INCLUDE[footer-include](../../../includes/footer-banner.md)] \ No newline at end of file +[!INCLUDE[footer-include](../../../includes/footer-banner.md)]