Operators
Operators overview
These following operator groups are listed from the highest priority group to the lowest priority group.
Primary operators
x.y- member accessx..y(<method args>)- member access cascade. Overrides result of methodywithx, which is useful for chaining method calls to a common target. For example:string..Append("name: ").Append(name);BothAppendcalls are targeted onstringeven thoughAppendreturnsvoid.x?.y- null conditional member access. Results in null ifxis null.f(x)- method calla[x]- array indexing
Unary operators
x++- postfix increment, increments x and results in result before the incrementx--- postfix decrement, decrements x and results in result before the decrement++x- prefix increment, increments x and results in the new value--x- prefix decrement, decrements x and results in the new value~x- bitwise complement!x- logical negation(T)x- type castsxto typeT&x- address ofx*x- dereference pointerxx(..y, <other method args>)- argument cascade. Overrides result of methodxwithy, which is useful for reusing an argument. For example, see string ease of use.
Multiplicative operators
x * y- multiplicationx &* y- multiplication with overflow checking disabled.x / y- division. Ifxandyare integers, results in an integer truncated toward zero.x % y- remainder. Ifxandyare integers, results in the remainder of the divisionx / y.
Additive operators
x + y- additionx &+ y- addition with overflow checking disabledx - y- subtractionx &- y- subtraction with overflow checking disabled. Allows for subtracing two unsigned integers without resulting in a signed integer.
Shift operators
x << y- shift bits leftx >> y- shift bits right. ifxis signed, the left bits are filled with the sign bit.
Spaceship operator
x <=> y- results is negative ifx < y, zero ifx == y, positive ifx > y
Comparison operators
x < yx > yx <= yx >= yx is T- results in true ifxcan be cast to typeTx as T- castsxtoTif the cast is possible, otherwise results in null
Logical AND operator
x & y- bitwise AND
Logical XOR operator
x ^ y- bitwise XOR
Logical OR operator
x | y- bitwise OR
Equality operator
x == yx === y- strict equalityx != yx !== y- strict inequality
The strict equality operators can be used to check reference equality, skipping any equality operator overloads. For value types such as structs or tuples, the strict equality operator will perform a memberwise strict equality check.
Conditional AND operator
x && y
Conditional OR operator
x || y
Null-coalescing operator
x ?? y- results inxif it is non-null, otherwise results iny
Conditional operator
x ? y : z- results inyisxis true, otherwise results inz
Assignment operators
Assignments result in the new value of x.
x = yx += yx -= yx *= yx /= yx %= yx |= yx ^= yx <<= yx >>= yx ??= y=> x- Method binding operator
Type attribute operators
sizeof(T)- size ofT. Note that reference types will always result in the native pointer sizealignof(T)- alignment ofTstrideof(T)- size ofT, aligned to the alignment ofTalloctype(T)- result ofnew T(), which will beTfor reference types andT*for valuetypescomptype(x)- type of reflected typexdecltype(x)- type of result of expressionx. Any expression is allowed, including method calls, but will only be evaluated to determine the type ofxand won’t generate any executable code.nullable(T)-TifTis already nullable, otherwiseT?rettype(T)- return type of a delegate or functiontypeof(T)- reflected type of typeToffsetof(T, field)- byte offset offieldinTnameof(T, field)- name offieldinT
Ref operators
ref x- required for passing values into ref parameters or other values expectingrefout x- required for passing values into out parametersvar x- create a new variablexfrom an out parameterlet x- create a new const/readonly variablexfrom an out parameter
Params operator
- params x - where x is a variadic parameter, will pass through those params to another variadic parameter. Where x is a delegate or function params, will expand those in place.
See Variable argument counts for examples.
Range operators
x...y- creates an inclusive range from x up to including yx..<y- creates an exclusive range from x up to but excluding yx...- creates an index range from x up to including the end of the collection...y- creates an index range from the start of the collection up to including yx...^y- creates an index range from x up to including y, but counted from the back (See Index operator below). For example from thexth element of the list to theyth, but counted from the back, element.
See Range expression for examples.
Index from end operator
^x- creates an Index for thexth element counting from the back (.FromEnd), where the last element is ^1 (^0 is equal to the count/length of the collection)
Index is mostly used for indexing collections and constructing ranges.
let list = scope List<int>() { 5, 1, 0 };
let first = list[0];
// first == 5
// Indexing from the back starts at Count (3 in this case), which is out of range. Thus we get the last element by counting down one -> ^1
let last = list[^1];
// last == 0
Casting
The (T)x cast operator can directly perform many kinds of type conversions, but there are some special cases:
* Unboxing. (T)obj where obj is an Object and T is a valuetype will perform an boxing. This unboxing can fail at runtime in Debug mode (when Dynamic Cast Checks are enabled). You can use an obj is T check or a obj as T? expression to safely unbox.
* Retrieving an object’s address: the expression (void*)obj where obj is an Object type is actually an unboxing request, not a type conversion. System.Internal.UnsafeCastToPtr can return the address of an Object as a void*.
* Casting to an unrelated type. Sometimes double-casts can be used to achieve what would otherwise be an illegal cast. For example, with (void*)handle where handle is a the typed primitive struct Handle : int, the cast directly to void* is not allowed, but (void*)(int)handle is allowed.
Operator overloading
Structs and classes can provide operator overloads. Comparison operator selection is flexible, in that not all combination of <, <=, ==, !=, >, and >= need to be defined. The “inverse” of operators will be called if available, or if just the <=> operator is defined then that can be used for all comparison types as well.
struct Vector2
{
float x;
float y;
public this(float x, float y)
{
this.x = x;
this.y = y;
}
/* Binary + operator */
public static Vector2 operator+(Vector2 lhs, Vector2 rhs)
{
return .(lhs.x + rhs.x, lhs.y + rhs.y);
}
/* Unary '-' operator */
public static Vector2 operator-(Vector2 val)
{
return .(-val.x, -val.y);
}
/* Unary '++' operator */
public static Vector2 operator++(Vector2 val)
{
return .(val.x + 1, val.y + 1);
}
/* Non-static unary '--' operator */
public void operator--() mut
{
x--;
y--;
}
/* Assignment '+=' operator */
public void operator+=(Vector2 rhs) mut
{
x += rhs.x;
y += rhs.y;
}
/* Comparison operator */
public static int operator<=>(Vector2 lhs, Vector2 rhs)
{
/* Compare on X, or on Y if X's are equal */
int cmp = lhs.x <=> rhs.x;
if (cmp != 0)
return cmp;
return lhs.y <=> rhs.y;
}
/* Conversion operator from float[2] */
public static operator Vector2(float[2] val)
{
return .(val[0], val[1]);
}
}
Binary operators can be marked with the [Commutable] attribute, which allows for certain operator transformations. A commutable “A < B” operator, for example, can be used for “B > A”, “!(A >= B)”, and !(B <= A). A commutable “A == B” operator can be used for “B == A”, “!(A != B)”, and “!(B != A)”.