Expressions
Allocations
new
and scope
keywords are used for allocation (see Memory Management)
append
The append
expression allocates memory immediately after an object’s allocated memory, and can only be used in a constructor. (See Memory Management)
An append
allocation can be used in any way a new
allocation can be used. (See new)
Assignment operations
Binary operations
See Binary operators
Bind expression =>
The =>
expression is used for method binding (see Method References)
box
The box
expression allocates an object which wraps a value type. (See Memory Management (boxing))
scope box x
- boxesx
in current scopescope:s box x
- boxesx
in scopes
new box x
- boxesx
in global allocatornew:a box x
- boxesx
in custom allocatora
case
case
expressions can be used for pattern matching outside switches. (See Pattern Matching)
Cast expression
(T)x
casts valuex
to typeT
Conditional operator
x ? y : z
- results iny
isx
is true, otherwise results inz
Conditional variable declarations
Variable declarations can be use as boolean expressions in ‘if’ statements for nullable types. These can be used with certain types of binary operations in the cases where a ‘true’ overall ‘if’ result ensures that the conditional variable declaration was also evaluated and resulted in ‘true’.
/* Simple conditional variable declaration */
if (let name = GetName())
{
}
/* 'Complex' conditional variable declaration */
if ((let name = GetName()) && (isEnabled))
{
}
/* This is ILLEGAL since "force" can cause the block to be entered even if the conditional variable declaration fails */
if ((let name == GetName()) || (force))
{
}
default
Every type has a “default” value, which is always zero-initialization.
// Default can specify a type and results in a default-initialized value
var str = default(String);
// Default will use the "expected" type if there is no explicit default type specified
String str2 = default;
Expression blocks
Expression blocks end with an expression that is not terminated by a semicolon.
Console.WriteLine("Result={}",
{
GetByRef(let val);
val
});
Index expressions
- ‘y = x[i]’ - Indexes value
x
by indexi
. Ifx
is a pointer, is equivalent toy = *(x + i)
. Otherwise, calls theget
method on thethis[int]
indexer property. x[i] = y
- Indexes valuex
by indexi
. Ifx
is a pointer, is equivalent to*(x + i) = y
. Otherwise, calls theset
method on thethis[int]
indexer property if there is aset
method, otherwise callsget
method if it returns aref
value.
Literals
123
- number0x1234
- hex number0x1234'5678
- number with a seperator, which can be placed anywhere0x1234L
- int64 hex number0x1234UL
- uint64 hex number'c'
- char8'😃'
- char32- 1.2f - float
- 2.3 - double
- “Hello” - String
new
The new
expression allocates memory in the global allocator or in a custom allocator. (See Memory Management)
new T(...)
- allocates instance ofT
in the global allocator. Result isT
ifT
is a reference type, otherwise result isT*
new T[i]
- allocates typeT[]
with array sizei
new T[i] (...)
- allocates typeT[]
with array sizei
and with an initializernew T[] (...)
- allocates typeT[]
whose size is based on the number of initializersnew T[i]*
- allocatesi
contiguous instances ofT
in the global allocator, a returns aT*
pointer to the first element. Note that the allocation size is typeof(T).InstanceStride*i for convenience, though technically this allocates extra padding to the end of the last element.new box x
- boxes valuex
in the global allocator. (See Memory Management (boxing))
All new
operations can also accept a custom allocator argument.
new:a T(....)
allocates an instance ofT
in custom allocatora
wherea
is an identifier.new:(a) T(...)
allocates an instance ofT
in custom allocatora
wherea
is any expression.
Null-conditional operators
Null-conditional operators val?.field
and val?[index]
will result in null
if val
is null. The null-conditional operators can be applied in a chain, which will short-circuit to null
at the first instance of a null
.
int? a = val?.intField;
int? nameLength = val?.name?.Length;
Parentheses expression
Adding parentheses around expressions can be used for changing order of operations for complex expressions.
int a = 1 + 2 * 3; // The multiply happens before the add here, resulting in 7
int b = (1 + 2) * 3; // The add happens before the multiply here, resulting in 9
Range expression
Ranges consist of a start and end integer value and are primarily used for loop iteration and range indexing. They can be created as inclusive or exclusive ranges. Index ranges may have one open side. (See Range operators)
let list = scope List<int>() { 5, 1, 0 };
/* Since we are iterating through a range, the list.Count getter will only be called once when the range is created */
/* Thus, the lists count is simply doubled instead of creating an infinite loop */
for (let i in 0 ..< list.Count)
list.Add(list[i]);
// list is now: { 5, 1, 0, 5, 1, 0 }
for (let i in (0 ..< list.Count).Reversed)
list.Add(list[i]);
// list is now: { 5, 1, 0, 5, 1, 0, 0, 1, 5, 0, 1, 5 }
var subset = list[...2]; // equivalent to 0...2, ..<3 and ...^10 (^ counts from the end, staring at Count)
subset = list[4...]; // equivalent to 4...11, 4...^1 and 4..<^0
scope
The scope
expression allocates memory on the stack, in a scope contained in an executing method. (See Memory Management)
A scope
allocation can be used in any way a new
allocation can be used. (See new)
this
this
is a special variable name, which represents the current instance in instance methods. If the defining type is a struct then this
is a value type (not a pointer), which is mutable for “mut” methods and immutable otherwise.
Tuple expression
The tuple expression is a paranthesized expression containing multiple comma-seperated values, and optionally field names. (See Data Types (Tuples)))
Unary operations
See unary operators
Uninitialized ‘?’
When assigned to a variable or field, ?
will cause the value to be treated as if it had an assignment but without (necessarily) any actual operation. This can be useful in cases such as with “buffer” type arrays that don’t need to be zero-initialized before use.
When used with out
parameters, ?
will act as a discard.
When used in constructors, uninitialized constructor calls this(...) : this(?)
and this(...) : base(?)
will discard the according initializers and constructors. See Initialization