Table of Contents
Assignment Operators
An assignment changes the contents of a variable or field.
The X++ assignment operators are shown in the following table. There is no difference between prefixed and postfixed operators.
Operator | Description |
---|---|
= |
Assigns the expression on the right of the equal sign to the variable on the left. |
+= |
Assigns the variable on the left the current variable value plus the expression on the right. |
++ |
Increments the variable by one. |
-= |
Assigns the variable on the left the current variable value minus the expression on the right. |
-- |
Decrements the variable by one. |
Assignment Operator Code Examples
// An example of assignment operators and their output. static void Example1() { int i = 1; // Using the = operator. i is assigned the value of i, plus 1. i = 2. i = i + 1; info(strFmt("Example 1: The result is "), i); // The result is 2. } static void Example2() { int i = 1; // Using the += operator. i is assigned the value of i, plus 1. // i = 2 (i = i + 1). i += 1; info(strFmt("Example 2: The result is "), i); // The result is 2. } static void Example3() { int i = 1; // Using the ++ operator. i is incremented by 1, and then // by 1 again in the second statement. The final value of i is 3. i++; ++i; info(strFmt("Example 3: The result is "), i); // The result is 3. } static void Example4() { int i = 1; // Using the -= operator. i is assigned the value of i minus 1. // i = 0 (i = i - 1). i -= 1; info(strFmt("Example 4: The result is "), i); // The result is 0. } static void Example5() { int i = 1; // Using the -- operator. i is decremented by 1, and then by // 1 again in the second statement. The final value of i is -1. i--; --i; info(strFmt("Example 5: The result is "), i); // The result is -1. } // Outputplacedhere Message ( _____ ) Info_a1: The result is 2 Info_a2: etc...
Arithmetic Operators
Use arithmetic operators to perform numeric calculations. All arithmetic operators are dyadic; that is, they work on two operands. The exception is ~
, which is unary.
Syntax: expression1 ArithmeticOperator expression2.
The arithmetic operators are as follows:
Operator | Description |
---|---|
<< | The left shift (<< ) operator performs expression2 left shift (a multiplication by 2) on expression1. |
>> | The right shift (>> ) operator performs expression2 right shift (a division by 2) on expression1. |
* | The multiply (* ) operator multiplies expression1 by expression2. |
/ | The divide (/ ) operator divides expression1 by expression2. |
DIV | The integer division (DIV ) operator performs an integer division of expression1 by expression2. |
MOD | The integer remainder (MOD ) operator returns the remainder of an integer division of expression1 by expression2. |
~ | The not (~ ) operator, or unary operator, performs a binary not-operation. |
& | The binary AND (& ) operator performs a binary and-operation on expression1 and expression2. |
^ | The binary XOR (^ ) operator performs a binary XOR-operation on expression1 and expression2. |
| | The binary OR (| ) operator performs a binary or-operation on expression1 and expression2. |
+ | The plus (+ ) operator adds expression1 to expression2. |
– | The minus (- ) operator subtracts expression2 from expression1. |
? | The ternary (? ) operator takes three expressions: expression1 ? expression2 : expression3. If expression1 is true, expression2 is returned; otherwise, expression3 is returned. |
Arithmetic Operators Code Examples
int a; a = 1 << 4; // Performs 4 left shifts on 1 (1*2*2*2*2). This gives: a=16. // An example of the >> operator. int b; b = 16 >> 4; // Performs 4 right shifts on 16 (16/2/2/2/2). This gives b=1. int c; c = 4*5; // Multiplies 4 by 5. c=20. int d; d = 20/5; // Divides 20 by 5. d=4. int e; e = 100 div 21; // Returns the integer division of 100 by 21. e=4 (4*21 = 84, remainder 16). int f; f = 100 mod 21; // Returns the remainder of the integer division of 100 by 21. f=16. int g; g = ~1; // Binary negates 1. This gives g=-2 (all bits are reversed). int h; h = 1 & 3; // Binary AND. Returns the bits in common in the two integers. h=1; int i; i = 1 | 3; // Binary OR. Return the bits set in either 1 or 3. i=3. int j; j = 1 ^ 3; // Binary XOR. Returns the bits set in 1 and NOT in 3 and vice versa. j=2. int k; k = 1 + 3; // Adds 1 and 3. k=4. int l; l = 3 - 1; // Subtracts 1 from 3. l=2. int m; m = (400>4) ? 1 : 5; // If (400>4) 1 is returned, else 5 is returned. As 400>4, 1 is returned. m=1.
Expression Operators: Is and As for Inheritance
The as and is expression operators control downcast assignments. Downcast assignments involve class or table inheritance. Assignment statements that implicitly downcast can cause errors that are difficult to predict and diagnose. You can use the as keyword to make your downcasts explicit. You can use the is keyword to test whether a downcast is valid at run time.
As Keyword
Use the as keyword for assignments that downcast from a base class variable to a derived class variable. The as keyword tells other programmers and the compiler that you believe the downcast will be valid during run time.
The compiler reports an error for downcast assignment statements that lack the as keyword.
At run time, the as keyword causes the downcast assignment statement to assign null if the downcast is invalid.
This is keyword is often used to safely test whether the as keyword will work.
Code Example for the As Keyword
In the following code example, the DerivedClass
class extends the BaseClass
class. The code example contains two valid assignments between its basec
and derivedc
variables. The upcast assignment to basec
does not need the as keyword, but the downcast assignment to derivedc
does need the as keyword. The following code would compile and run without errors.
static void AsTestJob33(Args _args) { // DerivedClass extends BaseClass. BaseClass basec; DerivedClass derivedc; // BottomClass extends DerivedClass. BottomClass bottomc; derivedc = new DerivedClass(); // AS is not needed for an upcast assignment like this. basec = derivedc; // AS is needed for a downcast assignment like this. derivedc = basec as DerivedClass; bottomc = new BottomClass(); // AS causes this invalid downcast to assign null. bottomc = basec as DerivedClass; }
Is Keyword
The is keyword ascertains whether an object is a subtype of a specified class. The is expression returns true if the object is a subtype of the class, or if the object is the same type as the class.
The compiler reports an error if an is keyword expression compares two types where neither is a subtype of the other, and they are not of the same type. The compiler reports a similar error for any plain assignment statement between two types where neither is a subtype of the other, and they are not of the same type.
At run time the type of variable that references the underlying object is irrelevant to the is keyword. The is keyword causes the system to check the object that the variable references, not the declared type of the variable that references the object.
Code Examples for the Is Keyword
The following code examples illustrate the conditions that control whether an is expression returns true or false. The code examples rely on the fact that the Form
class and the Query
class both extend the TreeNode
class.
For this section, the code example precedes the explanations, which are comments.
Form myForm = new Form(); info(strFmt("%1", (myForm is Query))); // The X++ compiler issues an error. The compiler ascertains that the // Form class and the Query class are not part of the same inheritance // hierarchy. Both the Form class and the Query extends the TreeNode class, // but neither Form nor Query is a subtype of the other. TreeNode myTreeNode = new TreeNode(); info(strFmt("%1", (myTreeNode is Form))); // The Infolog displays 0 during run time, where 0 means false. No supertype // object can be considered to also be of its subtype class. Form myForm; info(strFmt("%1", (myForm is Form))); // The Infolog displays 0 during run time, where 0 means false. A null // reference causes the is expression to return false. Form myForm = new Form(); info(strFmt("%1", (myForm is Form))); // The Infolog displays 1 during run time, where 1 means true. // An object is an instance of its own class type. Form myForm = new Form(); info(strFmt("%1", (myForm is TreeNode))); // The Infolog displays 1 during run time, where 1 means true. // Every subtype is also of its supertype. Form myForm = new Form(); TreeNode myTreeNode; myTreeNode = myForm; // Upcast. info(strFmt("%1", (myTreeNode is Form))); // The Infolog displays 1 during run time, where 1 means true. // The type of the underlying object is what matters in the is // expression, not the type of the variable that references the object.
Is and As Keywords Code Example
The following code example contains a common use of the is keyword. The as keyword is used after the is keyword verifies that the as keyword will the object. The is and as keywords are uppercase to make them more visible in this example.
static void IsKeywordJob46(Args _args) { DerivedClass derivedc; BaseClass basec; basec = new DerivedClass(); // An upcast. if (basec IS DerivedClass) { info("Test 1: (basec IS DerivedClass) is true. Good."); derivedc = basec AS DerivedClass; } basec = new BaseClass(); if (!(basec IS DerivedClass)) { info("Test 2: !(basec IS DerivedClass) is true. Good."); } } //Output to the Infolog Test 1: (basec IS DerivedClass) is true. Good. Test 2: (!(basec IS DerivedClass)) is true. Good.
Object Class is a Special Case
The Object
class can appear as a special case in inheritance functionality. The compiler has bypasses type checking for assignments to and from variables that are declared of type Object
. Some classes inherit from the Object
class, some inherit from another class, and some do not inherit from any class. The Dialog
class does not inherit from any class, yet the assignment and call statements in the following code example all work. The assignment would fail at compile time if it had been bank4 = dlog3;
, because the Bank
and Dialog
classes have no inheritance relationship to each other.
static void ObjectIsAsJob4(Args _args) { Bank bank4; Object obj2; Dialog dlog3 = new Dialog("Test 4."); obj2 = dlog3; // The assignment does work. obj2.run(false); // The call causes the dialog to display. info("Test 4a is finished."); }
The compiler performs only one small check on assignments to a variable that is declared of the Object
class. The compiler checks to make sure that the item being assigned to the Object
variable is an instance of a class. The compiler does not allow an instance of a table buffer to be assigned to the Object
variable. Also, the compiler does not allow primitive data types, such as int
or str
, to be assigned to the Object
variable.
Root Items in Table Inheritance
All tables inherit directly from the Common
system table, unless they explicitly inherit from a different table. The Common
table cannot be instantiated. The Common
table does not exist in the underlying physical database. The Common
table inherits from the xRecord
class, but only in a special way that is not appropriate for the is keyword or the as keyword.
As Keyword and Tables
When the as keyword is used to perform an invalid downcast among tables, the target variable references an unusable non-null entity. Any attempt to dereference the target variable will cause an error that stops the X++ program.
Is and As Keywords and Extended Data Types
Each extended data type has an Extends property. The style of inheritance that is controlled by this property differs from the style of inheritance that the is and as keywords are designed for.
Relational Operators
The following table lists the relational operators that can be used in X++. All relational operators. except !, are placed between two expressions: expression1 relationalOperator expression2. For example, while (a > 10)
.
Operator | Description |
---|---|
like |
The like relational operator returns true if expression1 is like expression2. |
== |
The equal relational operator returns true if both expressions are equal. |
>= |
The greater than or equal to relational operator returns true if expression1 is greater than or equal to expression2. |
<= |
The less than or equal to realational operator returns true if expression1 is less than or equal to expression2. |
> |
The greater than relational operator returns true if expression1 is greater than expression2. |
< |
The less than relational operator returns true if expression1 is less than expression2. |
!= |
The not equal relational operator returns true if expression1 is different from (that is, not equal to) expression2. |
&& |
The and relational operator returns true if both expression1 and expression2 are true. |
|| |
The or relational operator returns true if expression1 or expression2 or both are true. |
! |
The not or unary relational operator negates the expression. It returns true if the expression is false or false if the expression is true. |
Like Relational Operator (continued)
like
can use * as a wildcard for zero or more characters and ? as a wildcard for one character. Expression2 cannot be longer than 1000 characters.
If the expressions that you are comparing contain a file path, you need to include four backslashes between each element. For example:
select * from xRefpaths
where xRefPaths.Path like “\\\\Classes\\\\AddressSelectForm”
like
is evaluated by the underlying SQL, so the result may differ on different installations.
Equal Relational Operator (continued)
When you use ==
to compare objects, the object references rather than the objects themselves are compared. This may be a problem if you compare two objects that are located on the server and on the client, respectively. In such cases, you should use the equal
method in the Object
class, which you can override to specify what it means that two objects are equal. If equal
is not overridden, the comparison is identical to the one performed by ==
.
Relational Operator Code Examples
"Jones" like "Jo?es" // Returns true; the ? is equal to any single character. "Fabrikam, Inc." like "Fa*" // Returns true; the * is equal to zero or more characters. (( 42 * 2) == 84) // Returns true; 42*2 is equal to 84. today() >= 1\1\1980 // Returns true; today is later than January 1, 1980. ((11 div 10) >= 1) // Returns true; ; 11 div 10 is 1 (therefore, >= 1 is true). (11<= 12) // Returns true; 11 is less than 12. ((11 div 10) > 1) // Returns false; 11 div 10 is 1. (11 div 10) < 1) // Returns false; 11 div 10 is 1. (11 != 12) // Returns true; 11 is not equal to 12. (1 == 1) && (3 > 1) // Returns true; both expressions are true.
Operator Precedence
The order in which a compound expression is evaluated can be important. For example, (x + y / 100)
gives a different result depending on whether the addition or the division is performed first. You can use parentheses ( )
to explicitly tell the compiler how you want an expression to be evaluated. For example, (x + y)/ 100
. If you do not explicitly tell the compiler the order that you want operations to be performed in, the order is based on the precedence assigned to the operators. For example, the division operator has a higher precedence than the addition operator. For x + y / 100
, the compiler would evaluate y/100
first. So, x + y / 100
is equivalent to x + (y / 100)
. To make your code easy to read and maintain, be explicit. Indicate with parentheses which operators should be evaluated first.
Order of Operator Precedence
The operators in the following table are listed in precedence order—the higher in the table an operator appears, the higher its precedence. Operators with higher precedence are evaluated before operators with a lower precedence. Note that the operator precedence of X++ is not the same as other languages, for example C# and Java.
Operators in precedence order | Syntax |
---|---|
unary operators | - ~ ! |
multiplicative, shift, bitwise AND, bitwise exclusive OR | * / % DIV << >> & ^ |
additive, bitwise inclusive OR | + – | |
relational, equality | < <= == != > >= like as is |
logical operators (AND, OR) | && || |
conditional | ? : |
Operators on the same line have equal precedence. If there is more than one of these operators in an expression, the expression is evaluated from left to right unless assignment operators are used (these are evaluated from right to left).
For example, &&
(logical AND) and ||
(logical OR) have the same precedence and are evaluated from left to right. This means that:
0&&0||1 == 1
, and 1||0&&0 == 0