csharplang/spec/variables.md

642 lines
45 KiB
Markdown
Raw Permalink Normal View History

# Variables
Variables represent storage locations. Every variable has a type that determines what values can be stored in the variable. C# is a type-safe language, and the C# compiler guarantees that values stored in variables are always of the appropriate type. The value of a variable can be changed through assignment or through use of the `++` and `--` operators.
A variable must be ***definitely assigned*** ([Definite assignment](variables.md#definite-assignment)) before its value can be obtained.
As described in the following sections, variables are either ***initially assigned*** or ***initially unassigned***. An initially assigned variable has a well-defined initial value and is always considered definitely assigned. An initially unassigned variable has no initial value. For an initially unassigned variable to be considered definitely assigned at a certain location, an assignment to the variable must occur in every possible execution path leading to that location.
## Variable categories
C# defines seven categories of variables: static variables, instance variables, array elements, value parameters, reference parameters, output parameters, and local variables. The sections that follow describe each of these categories.
In the example
```csharp
class A
{
2018-08-12 23:46:56 +02:00
public static int x;
int y;
2018-08-12 23:46:56 +02:00
void F(int[] v, int a, ref int b, out int c) {
int i = 1;
c = a + b++;
}
}
```
`x` is a static variable, `y` is an instance variable, `v[0]` is an array element, `a` is a value parameter, `b` is a reference parameter, `c` is an output parameter, and `i` is a local variable.
### Static variables
A field declared with the `static` modifier is called a ***static variable***. A static variable comes into existence before execution of the static constructor ([Static constructors](classes.md#static-constructors)) for its containing type, and ceases to exist when the associated application domain ceases to exist.
The initial value of a static variable is the default value ([Default values](variables.md#default-values)) of the variable's type.
For purposes of definite assignment checking, a static variable is considered initially assigned.
### Instance variables
A field declared without the `static` modifier is called an ***instance variable***.
#### Instance variables in classes
An instance variable of a class comes into existence when a new instance of that class is created, and ceases to exist when there are no references to that instance and the instance's destructor (if any) has executed.
The initial value of an instance variable of a class is the default value ([Default values](variables.md#default-values)) of the variable's type.
For the purpose of definite assignment checking, an instance variable of a class is considered initially assigned.
#### Instance variables in structs
An instance variable of a struct has exactly the same lifetime as the struct variable to which it belongs. In other words, when a variable of a struct type comes into existence or ceases to exist, so too do the instance variables of the struct.
The initial assignment state of an instance variable of a struct is the same as that of the containing struct variable. In other words, when a struct variable is considered initially assigned, so too are its instance variables, and when a struct variable is considered initially unassigned, its instance variables are likewise unassigned.
### Array elements
The elements of an array come into existence when an array instance is created, and cease to exist when there are no references to that array instance.
The initial value of each of the elements of an array is the default value ([Default values](variables.md#default-values)) of the type of the array elements.
For the purpose of definite assignment checking, an array element is considered initially assigned.
### Value parameters
A parameter declared without a `ref` or `out` modifier is a ***value parameter***.
A value parameter comes into existence upon invocation of the function member (method, instance constructor, accessor, or operator) or anonymous function to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter normally ceases to exist upon return of the function member or anonymous function. However, if the value parameter is captured by an anonymous function ([Anonymous function expressions](expressions.md#anonymous-function-expressions)), its life time extends at least until the delegate or expression tree created from that anonymous function is eligible for garbage collection.
For the purpose of definite assignment checking, a value parameter is considered initially assigned.
### Reference parameters
A parameter declared with a `ref` modifier is a ***reference parameter***.
A reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the function member or anonymous function invocation. Thus, the value of a reference parameter is always the same as the underlying variable.
The following definite assignment rules apply to reference parameters. Note the different rules for output parameters described in [Output parameters](variables.md#output-parameters).
* A variable must be definitely assigned ([Definite assignment](variables.md#definite-assignment)) before it can be passed as a reference parameter in a function member or delegate invocation.
* Within a function member or anonymous function, a reference parameter is considered initially assigned.
Within an instance method or instance accessor of a struct type, the `this` keyword behaves exactly as a reference parameter of the struct type ([This access](expressions.md#this-access)).
### Output parameters
A parameter declared with an `out` modifier is an ***output parameter***.
An output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the function member or delegate invocation. Thus, the value of an output parameter is always the same as the underlying variable.
The following definite assignment rules apply to output parameters. Note the different rules for reference parameters described in [Reference parameters](variables.md#reference-parameters).
* A variable need not be definitely assigned before it can be passed as an output parameter in a function member or delegate invocation.
* Following the normal completion of a function member or delegate invocation, each variable that was passed as an output parameter is considered assigned in that execution path.
* Within a function member or anonymous function, an output parameter is considered initially unassigned.
* Every output parameter of a function member or anonymous function must be definitely assigned ([Definite assignment](variables.md#definite-assignment)) before the function member or anonymous function returns normally.
Within an instance constructor of a struct type, the `this` keyword behaves exactly as an output parameter of the struct type ([This access](expressions.md#this-access)).
### Local variables
A ***local variable*** is declared by a *local_variable_declaration*, which may occur in a *block*, a *for_statement*, a *switch_statement* or a *using_statement*; or by a *foreach_statement* or a *specific_catch_clause* for a *try_statement*.
The lifetime of a local variable is the portion of program execution during which storage is guaranteed to be reserved for it. This lifetime extends at least from entry into the *block*, *for_statement*, *switch_statement*, *using_statement*, *foreach_statement*, or *specific_catch_clause* with which it is associated, until execution of that *block*, *for_statement*, *switch_statement*, *using_statement*, *foreach_statement*, or *specific_catch_clause* ends in any way. (Entering an enclosed *block* or calling a method suspends, but does not end, execution of the current *block*, *for_statement*, *switch_statement*, *using_statement*, *foreach_statement*, or *specific_catch_clause*.) If the local variable is captured by an anonymous function ([Captured outer variables](expressions.md#captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from the anonymous function, along with any other objects that come to reference the captured variable, are eligible for garbage collection.
If the parent *block*, *for_statement*, *switch_statement*, *using_statement*, *foreach_statement*, or *specific_catch_clause* is entered recursively, a new instance of the local variable is created each time, and its *local_variable_initializer*, if any, is evaluated each time.
A local variable introduced by a *local_variable_declaration* is not automatically initialized and thus has no default value. For the purpose of definite assignment checking, a local variable introduced by a *local_variable_declaration* is considered initially unassigned. A *local_variable_declaration* may include a *local_variable_initializer*, in which case the variable is considered definitely assigned only after the initializing expression ([Declaration statements](variables.md#declaration-statements)).
2018-01-25 22:12:45 +01:00
Within the scope of a local variable introduced by a *local_variable_declaration*, it is a compile-time error to refer to that local variable in a textual position that precedes its *local_variable_declarator*. If the local variable declaration is implicit ([Local variable declarations](statements.md#local-variable-declarations)), it is also an error to refer to the variable within its *local_variable_declarator*.
A local variable introduced by a *foreach_statement* or a *specific_catch_clause* is considered definitely assigned in its entire scope.
The actual lifetime of a local variable is implementation-dependent. For example, a compiler might statically determine that a local variable in a block is only used for a small portion of that block. Using this analysis, the compiler could generate code that results in the variable's storage having a shorter lifetime than its containing block.
The storage referred to by a local reference variable is reclaimed independently of the lifetime of that local reference variable ([Automatic memory management](basic-concepts.md#automatic-memory-management)).
## Default values
The following categories of variables are automatically initialized to their default values:
* Static variables.
* Instance variables of class instances.
* Array elements.
The default value of a variable depends on the type of the variable and is determined as follows:
* For a variable of a *value_type*, the default value is the same as the value computed by the *value_type*'s default constructor ([Default constructors](types.md#default-constructors)).
* For a variable of a *reference_type*, the default value is `null`.
Initialization to default values is typically done by having the memory manager or garbage collector initialize memory to all-bits-zero before it is allocated for use. For this reason, it is convenient to use all-bits-zero to represent the null reference.
## Definite assignment
At a given location in the executable code of a function member, a variable is said to be ***definitely assigned*** if the compiler can prove, by a particular static flow analysis ([Precise rules for determining definite assignment](variables.md#precise-rules-for-determining-definite-assignment)), that the variable has been automatically initialized or has been the target of at least one assignment. Informally stated, the rules of definite assignment are:
* An initially assigned variable ([Initially assigned variables](variables.md#initially-assigned-variables)) is always considered definitely assigned.
* An initially unassigned variable ([Initially unassigned variables](variables.md#initially-unassigned-variables)) is considered definitely assigned at a given location if all possible execution paths leading to that location contain at least one of the following:
* A simple assignment ([Simple assignment](expressions.md#simple-assignment)) in which the variable is the left operand.
* An invocation expression ([Invocation expressions](expressions.md#invocation-expressions)) or object creation expression ([Object creation expressions](expressions.md#object-creation-expressions)) that passes the variable as an output parameter.
* For a local variable, a local variable declaration ([Local variable declarations](statements.md#local-variable-declarations)) that includes a variable initializer.
The formal specification underlying the above informal rules is described in [Initially assigned variables](variables.md#initially-assigned-variables), [Initially unassigned variables](variables.md#initially-unassigned-variables), and [Precise rules for determining definite assignment](variables.md#precise-rules-for-determining-definite-assignment).
The definite assignment states of instance variables of a *struct_type* variable are tracked individually as well as collectively. In addition to the rules above, the following rules apply to *struct_type* variables and their instance variables:
* An instance variable is considered definitely assigned if its containing *struct_type* variable is considered definitely assigned.
* A *struct_type* variable is considered definitely assigned if each of its instance variables is considered definitely assigned.
Definite assignment is a requirement in the following contexts:
* A variable must be definitely assigned at each location where its value is obtained. This ensures that undefined values never occur. The occurrence of a variable in an expression is considered to obtain the value of the variable, except when
* the variable is the left operand of a simple assignment,
* the variable is passed as an output parameter, or
* the variable is a *struct_type* variable and occurs as the left operand of a member access.
* A variable must be definitely assigned at each location where it is passed as a reference parameter. This ensures that the function member being invoked can consider the reference parameter initially assigned.
* All output parameters of a function member must be definitely assigned at each location where the function member returns (through a `return` statement or through execution reaching the end of the function member body). This ensures that function members do not return undefined values in output parameters, thus enabling the compiler to consider a function member invocation that takes a variable as an output parameter equivalent to an assignment to the variable.
* The `this` variable of a *struct_type* instance constructor must be definitely assigned at each location where that instance constructor returns.
### Initially assigned variables
The following categories of variables are classified as initially assigned:
* Static variables.
* Instance variables of class instances.
* Instance variables of initially assigned struct variables.
* Array elements.
* Value parameters.
* Reference parameters.
* Variables declared in a `catch` clause or a `foreach` statement.
### Initially unassigned variables
The following categories of variables are classified as initially unassigned:
* Instance variables of initially unassigned struct variables.
* Output parameters, including the `this` variable of struct instance constructors.
* Local variables, except those declared in a `catch` clause or a `foreach` statement.
### Precise rules for determining definite assignment
In order to determine that each used variable is definitely assigned, the compiler must use a process that is equivalent to the one described in this section.
The compiler processes the body of each function member that has one or more initially unassigned variables. For each initially unassigned variable *v*, the compiler determines a ***definite assignment state*** for *v* at each of the following points in the function member:
* At the beginning of each statement
* At the end point ([End points and reachability](statements.md#end-points-and-reachability)) of each statement
* On each arc which transfers control to another statement or to the end point of a statement
* At the beginning of each expression
* At the end of each expression
The definite assignment state of *v* can be either:
* Definitely assigned. This indicates that on all possible control flows to this point, *v* has been assigned a value.
* Not definitely assigned. For the state of a variable at the end of an expression of type `bool`, the state of a variable that isn't definitely assigned may (but doesn't necessarily) fall into one of the following sub-states:
* Definitely assigned after true expression. This state indicates that *v* is definitely assigned if the boolean expression evaluated as true, but is not necessarily assigned if the boolean expression evaluated as false.
* Definitely assigned after false expression. This state indicates that *v* is definitely assigned if the boolean expression evaluated as false, but is not necessarily assigned if the boolean expression evaluated as true.
The following rules govern how the state of a variable *v* is determined at each location.
#### General rules for statements
* *v* is not definitely assigned at the beginning of a function member body.
* *v* is definitely assigned at the beginning of any unreachable statement.
* The definite assignment state of *v* at the beginning of any other statement is determined by checking the definite assignment state of *v* on all control flow transfers that target the beginning of that statement. If (and only if) *v* is definitely assigned on all such control flow transfers, then *v* is definitely assigned at the beginning of the statement. The set of possible control flow transfers is determined in the same way as for checking statement reachability ([End points and reachability](statements.md#end-points-and-reachability)).
* The definite assignment state of *v* at the end point of a block, `checked`, `unchecked`, `if`, `while`, `do`, `for`, `foreach`, `lock`, `using`, or `switch` statement is determined by checking the definite assignment state of *v* on all control flow transfers that target the end point of that statement. If *v* is definitely assigned on all such control flow transfers, then *v* is definitely assigned at the end point of the statement. Otherwise; *v* is not definitely assigned at the end point of the statement. The set of possible control flow transfers is determined in the same way as for checking statement reachability ([End points and reachability](statements.md#end-points-and-reachability)).
#### Block statements, checked, and unchecked statements
The definite assignment state of *v* on the control transfer to the first statement of the statement list in the block (or to the end point of the block, if the statement list is empty) is the same as the definite assignment statement of *v* before the block, `checked`, or `unchecked` statement.
#### Expression statements
For an expression statement *stmt* that consists of the expression *expr*:
* *v* has the same definite assignment state at the beginning of *expr* as at the beginning of *stmt*.
* If *v* if definitely assigned at the end of *expr*, it is definitely assigned at the end point of *stmt*; otherwise; it is not definitely assigned at the end point of *stmt*.
#### Declaration statements
* If *stmt* is a declaration statement without initializers, then *v* has the same definite assignment state at the end point of *stmt* as at the beginning of *stmt*.
* If *stmt* is a declaration statement with initializers, then the definite assignment state for *v* is determined as if *stmt* were a statement list, with one assignment statement for each declaration with an initializer (in the order of declaration).
#### If statements
For an `if` statement *stmt* of the form:
```csharp
if ( expr ) then_stmt else else_stmt
```
* *v* has the same definite assignment state at the beginning of *expr* as at the beginning of *stmt*.
* If *v* is definitely assigned at the end of *expr*, then it is definitely assigned on the control flow transfer to *then_stmt* and to either *else_stmt* or to the end-point of *stmt* if there is no else clause.
* If *v* has the state "definitely assigned after true expression" at the end of *expr*, then it is definitely assigned on the control flow transfer to *then_stmt*, and not definitely assigned on the control flow transfer to either *else_stmt* or to the end-point of *stmt* if there is no else clause.
* If *v* has the state "definitely assigned after false expression" at the end of *expr*, then it is definitely assigned on the control flow transfer to *else_stmt*, and not definitely assigned on the control flow transfer to *then_stmt*. It is definitely assigned at the end-point of *stmt* if and only if it is definitely assigned at the end-point of *then_stmt*.
* Otherwise, *v* is considered not definitely assigned on the control flow transfer to either the *then_stmt* or *else_stmt*, or to the end-point of *stmt* if there is no else clause.
#### Switch statements
In a `switch` statement *stmt* with a controlling expression *expr*:
* The definite assignment state of *v* at the beginning of *expr* is the same as the state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* on the control flow transfer to a reachable switch block statement list is the same as the definite assignment state of *v* at the end of *expr*.
#### While statements
For a `while` statement *stmt* of the form:
```csharp
while ( expr ) while_body
```
* *v* has the same definite assignment state at the beginning of *expr* as at the beginning of *stmt*.
* If *v* is definitely assigned at the end of *expr*, then it is definitely assigned on the control flow transfer to *while_body* and to the end point of *stmt*.
* If *v* has the state "definitely assigned after true expression" at the end of *expr*, then it is definitely assigned on the control flow transfer to *while_body*, but not definitely assigned at the end-point of *stmt*.
* If *v* has the state "definitely assigned after false expression" at the end of *expr*, then it is definitely assigned on the control flow transfer to the end point of *stmt*, but not definitely assigned on the control flow transfer to *while_body*.
#### Do statements
For a `do` statement *stmt* of the form:
```csharp
do do_body while ( expr ) ;
```
* *v* has the same definite assignment state on the control flow transfer from the beginning of *stmt* to *do_body* as at the beginning of *stmt*.
* *v* has the same definite assignment state at the beginning of *expr* as at the end point of *do_body*.
* If *v* is definitely assigned at the end of *expr*, then it is definitely assigned on the control flow transfer to the end point of *stmt*.
* If *v* has the state "definitely assigned after false expression" at the end of *expr*, then it is definitely assigned on the control flow transfer to the end point of *stmt*.
#### For statements
Definite assignment checking for a `for` statement of the form:
```csharp
for ( for_initializer ; for_condition ; for_iterator ) embedded_statement
```
is done as if the statement were written:
```csharp
{
2018-08-12 23:46:56 +02:00
for_initializer ;
while ( for_condition ) {
embedded_statement ;
for_iterator ;
}
}
```
If the *for_condition* is omitted from the `for` statement, then evaluation of definite assignment proceeds as if *for_condition* were replaced with `true` in the above expansion.
#### Break, continue, and goto statements
The definite assignment state of *v* on the control flow transfer caused by a `break`, `continue`, or `goto` statement is the same as the definite assignment state of *v* at the beginning of the statement.
#### Throw statements
For a statement *stmt* of the form
```csharp
throw expr ;
```
The definite assignment state of *v* at the beginning of *expr* is the same as the definite assignment state of *v* at the beginning of *stmt*.
#### Return statements
For a statement *stmt* of the form
```csharp
return expr ;
```
* The definite assignment state of *v* at the beginning of *expr* is the same as the definite assignment state of *v* at the beginning of *stmt*.
* If *v* is an output parameter, then it must be definitely assigned either:
* after *expr*
* or at the end of the `finally` block of a `try`-`finally` or `try`-`catch`-`finally` that encloses the `return` statement.
For a statement stmt of the form:
```csharp
return ;
```
* If *v* is an output parameter, then it must be definitely assigned either:
* before *stmt*
* or at the end of the `finally` block of a `try`-`finally` or `try`-`catch`-`finally` that encloses the `return` statement.
#### Try-catch statements
For a statement *stmt* of the form:
```csharp
try try_block
catch(...) catch_block_1
...
catch(...) catch_block_n
```
* The definite assignment state of *v* at the beginning of *try_block* is the same as the definite assignment state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* at the beginning of *catch_block_i* (for any *i*) is the same as the definite assignment state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* at the end-point of *stmt* is definitely assigned if (and only if) *v* is definitely assigned at the end-point of *try_block* and every *catch_block_i* (for every *i* from 1 to *n*).
#### Try-finally statements
For a `try` statement *stmt* of the form:
```csharp
try try_block finally finally_block
```
* The definite assignment state of *v* at the beginning of *try_block* is the same as the definite assignment state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* at the beginning of *finally_block* is the same as the definite assignment state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* at the end-point of *stmt* is definitely assigned if (and only if) at least one of the following is true:
* *v* is definitely assigned at the end-point of *try_block*
* *v* is definitely assigned at the end-point of *finally_block*
If a control flow transfer (for example, a `goto` statement) is made that begins within *try_block*, and ends outside of *try_block*, then *v* is also considered definitely assigned on that control flow transfer if *v* is definitely assigned at the end-point of *finally_block*. (This is not an only if—if *v* is definitely assigned for another reason on this control flow transfer, then it is still considered definitely assigned.)
#### Try-catch-finally statements
Definite assignment analysis for a `try`-`catch`-`finally` statement of the form:
```csharp
try try_block
catch(...) catch_block_1
...
catch(...) catch_block_n
finally *finally_block*
```
is done as if the statement were a `try`-`finally` statement enclosing a `try`-`catch` statement:
```csharp
try {
2018-08-12 23:46:56 +02:00
try try_block
catch(...) catch_block_1
...
catch(...) catch_block_n
}
finally finally_block
```
The following example demonstrates how the different blocks of a `try` statement ([The try statement](statements.md#the-try-statement)) affect definite assignment.
```csharp
class A
{
2018-08-12 23:46:56 +02:00
static void F() {
int i, j;
try {
goto LABEL;
// neither i nor j definitely assigned
i = 1;
// i definitely assigned
}
catch {
// neither i nor j definitely assigned
i = 3;
// i definitely assigned
}
finally {
// neither i nor j definitely assigned
j = 5;
// j definitely assigned
}
// i and j definitely assigned
LABEL:;
// j definitely assigned
}
}
```
#### Foreach statements
For a `foreach` statement *stmt* of the form:
```csharp
foreach ( type identifier in expr ) embedded_statement
```
* The definite assignment state of *v* at the beginning of *expr* is the same as the state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* on the control flow transfer to *embedded_statement* or to the end point of *stmt* is the same as the state of *v* at the end of *expr*.
#### Using statements
For a `using` statement *stmt* of the form:
```csharp
using ( resource_acquisition ) embedded_statement
```
* The definite assignment state of *v* at the beginning of *resource_acquisition* is the same as the state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* on the control flow transfer to *embedded_statement* is the same as the state of *v* at the end of *resource_acquisition*.
#### Lock statements
For a `lock` statement *stmt* of the form:
```csharp
lock ( expr ) embedded_statement
```
* The definite assignment state of *v* at the beginning of *expr* is the same as the state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* on the control flow transfer to *embedded_statement* is the same as the state of *v* at the end of *expr*.
#### Yield statements
For a `yield return` statement *stmt* of the form:
```csharp
yield return expr ;
```
* The definite assignment state of *v* at the beginning of *expr* is the same as the state of *v* at the beginning of *stmt*.
* The definite assignment state of *v* at the end of *stmt* is the same as the state of *v* at the end of *expr*.
* A `yield break` statement has no effect on the definite assignment state.
#### General rules for simple expressions
The following rule applies to these kinds of expressions: literals ([Literals](expressions.md#literals)), simple names ([Simple names](expressions.md#simple-names)), member access expressions ([Member access](expressions.md#member-access)), non-indexed base access expressions ([Base access](expressions.md#base-access)), `typeof` expressions ([The typeof operator](expressions.md#the-typeof-operator)), default value expressions ([Default value expressions](expressions.md#default-value-expressions)) and `nameof` expressions ([Nameof expressions](expressions.md#nameof-expressions)).
* The definite assignment state of *v* at the end of such an expression is the same as the definite assignment state of *v* at the beginning of the expression.
#### General rules for expressions with embedded expressions
2017-10-14 00:21:44 +02:00
The following rules apply to these kinds of expressions: parenthesized expressions ([Parenthesized expressions](expressions.md#parenthesized-expressions)), element access expressions ([Element access](expressions.md#element-access)), base access expressions with indexing ([Base access](expressions.md#base-access)), increment and decrement expressions ([Postfix increment and decrement operators](expressions.md#postfix-increment-and-decrement-operators), [Prefix increment and decrement operators](expressions.md#prefix-increment-and-decrement-operators)), cast expressions ([Cast expressions](expressions.md#cast-expressions)), unary `+`, `-`, `~`, `*` expressions, binary `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `<`, `<=`, `>`, `>=`, `==`, `!=`, `is`, `as`, `&`, `|`, `^` expressions ([Arithmetic operators](expressions.md#arithmetic-operators), [Shift operators](expressions.md#shift-operators), [Relational and type-testing operators](expressions.md#relational-and-type-testing-operators), [Logical operators](expressions.md#logical-operators)), compound assignment expressions ([Compound assignment](expressions.md#compound-assignment)), `checked` and `unchecked` expressions ([The checked and unchecked operators](expressions.md#the-checked-and-unchecked-operators)), plus array and delegate creation expressions ([The new operator](expressions.md#the-new-operator)).
Each of these expressions has one or more sub-expressions that are unconditionally evaluated in a fixed order. For example, the binary `%` operator evaluates the left hand side of the operator, then the right hand side. An indexing operation evaluates the indexed expression, and then evaluates each of the index expressions, in order from left to right. For an expression *expr*, which has sub-expressions *e1, e2, ..., eN*, evaluated in that order:
* The definite assignment state of *v* at the beginning of *e1* is the same as the definite assignment state at the beginning of *expr*.
* The definite assignment state of *v* at the beginning of *ei* (*i* greater than one) is the same as the definite assignment state at the end of the previous sub-expression.
* The definite assignment state of *v* at the end of *expr* is the same as the definite assignment state at the end of *eN*
#### Invocation expressions and object creation expressions
For an invocation expression *expr* of the form:
```csharp
primary_expression ( arg1 , arg2 , ... , argN )
```
or an object creation expression of the form:
```csharp
new type ( arg1 , arg2 , ... , argN )
```
* For an invocation expression, the definite assignment state of *v* before *primary_expression* is the same as the state of *v* before *expr*.
* For an invocation expression, the definite assignment state of *v* before *arg1* is the same as the state of *v* after *primary_expression*.
* For an object creation expression, the definite assignment state of *v* before *arg1* is the same as the state of *v* before *expr*.
* For each argument *argi*, the definite assignment state of *v* after *argi* is determined by the normal expression rules, ignoring any `ref` or `out` modifiers.
* For each argument *argi* for any *i* greater than one, the definite assignment state of *v* before *argi* is the same as the state of *v* after the previous *arg*.
* If the variable *v* is passed as an `out` argument (i.e., an argument of the form `out v`) in any of the arguments, then the state of *v* after *expr* is definitely assigned. Otherwise; the state of *v* after *expr* is the same as the state of *v* after *argN*.
* For array initializers ([Array creation expressions](expressions.md#array-creation-expressions)), object initializers ([Object initializers](expressions.md#object-initializers)), collection initializers ([Collection initializers](expressions.md#collection-initializers)) and anonymous object initializers ([Anonymous object creation expressions](expressions.md#anonymous-object-creation-expressions)), the definite assignment state is determined by the expansion that these constructs are defined in terms of.
#### Simple assignment expressions
For an expression *expr* of the form `w = expr_rhs`:
* The definite assignment state of *v* before *expr_rhs* is the same as the definite assignment state of *v* before *expr*.
* The definite assignment state of *v* after *expr* is determined by:
* If *w* is the same variable as *v*, then the definite assignment state of *v* after *expr* is definitely assigned.
* Otherwise, if the assignment occurs within the instance constructor of a struct type, if *w* is a property access designating an automatically implemented property *P* on the instance being constructed and *v* is the hidden backing field of *P*, then the definite assignment state of *v* after *expr* is definitely assigned.
* Otherwise, the definite assignment state of *v* after *expr* is the same as the definite assignment state of *v* after *expr_rhs*.
#### && (conditional AND) expressions
For an expression *expr* of the form `expr_first && expr_second`:
* The definite assignment state of *v* before *expr_first* is the same as the definite assignment state of *v* before *expr*.
* The definite assignment state of *v* before *expr_second* is definitely assigned if the state of *v* after *expr_first* is either definitely assigned or "definitely assigned after true expression". Otherwise, it is not definitely assigned.
* The definite assignment state of *v* after *expr* is determined by:
* If *expr_first* is a constant expression with the value `false`, then the definite assignment state of *v* after *expr* is the same as the definite assignment state of *v* after *expr_first*.
* Otherwise, if the state of *v* after *expr_first* is definitely assigned, then the state of *v* after *expr* is definitely assigned.
* Otherwise, if the state of *v* after *expr_second* is definitely assigned, and the state of *v* after *expr_first* is "definitely assigned after false expression", then the state of *v* after *expr* is definitely assigned.
* Otherwise, if the state of *v* after *expr_second* is definitely assigned or "definitely assigned after true expression", then the state of *v* after *expr* is "definitely assigned after true expression".
* Otherwise, if the state of *v* after *expr_first* is "definitely assigned after false expression", and the state of *v* after *expr_second* is "definitely assigned after false expression", then the state of *v* after *expr* is "definitely assigned after false expression".
* Otherwise, the state of *v* after *expr* is not definitely assigned.
In the example
```csharp
class A
{
static void F(int x, int y) {
int i;
if (x >= 0 && (i = y) >= 0) {
// i definitely assigned
}
else {
// i not definitely assigned
}
// i not definitely assigned
}
}
```
the variable `i` is considered definitely assigned in one of the embedded statements of an `if` statement but not in the other. In the `if` statement in method `F`, the variable `i` is definitely assigned in the first embedded statement because execution of the expression `(i = y)` always precedes execution of this embedded statement. In contrast, the variable `i` is not definitely assigned in the second embedded statement, since `x >= 0` might have tested false, resulting in the variable `i` being unassigned.
#### || (conditional OR) expressions
For an expression *expr* of the form `expr_first || expr_second`:
* The definite assignment state of *v* before *expr_first* is the same as the definite assignment state of *v* before *expr*.
* The definite assignment state of *v* before *expr_second* is definitely assigned if the state of *v* after *expr_first* is either definitely assigned or "definitely assigned after false expression". Otherwise, it is not definitely assigned.
* The definite assignment statement of *v* after *expr* is determined by:
* If *expr_first* is a constant expression with the value `true`, then the definite assignment state of *v* after *expr* is the same as the definite assignment state of *v* after *expr_first*.
* Otherwise, if the state of *v* after *expr_first* is definitely assigned, then the state of *v* after *expr* is definitely assigned.
* Otherwise, if the state of *v* after *expr_second* is definitely assigned, and the state of *v* after *expr_first* is "definitely assigned after true expression", then the state of *v* after *expr* is definitely assigned.
* Otherwise, if the state of *v* after *expr_second* is definitely assigned or "definitely assigned after false expression", then the state of *v* after *expr* is "definitely assigned after false expression".
* Otherwise, if the state of *v* after *expr_first* is "definitely assigned after true expression", and the state of *v* after *expr_second* is "definitely assigned after true expression", then the state of *v* after *expr* is "definitely assigned after true expression".
* Otherwise, the state of *v* after *expr* is not definitely assigned.
In the example
```csharp
class A
{
static void G(int x, int y) {
int i;
if (x >= 0 || (i = y) >= 0) {
// i not definitely assigned
}
else {
// i definitely assigned
}
// i not definitely assigned
}
}
```
the variable `i` is considered definitely assigned in one of the embedded statements of an `if` statement but not in the other. In the `if` statement in method `G`, the variable `i` is definitely assigned in the second embedded statement because execution of the expression `(i = y)` always precedes execution of this embedded statement. In contrast, the variable `i` is not definitely assigned in the first embedded statement, since `x >= 0` might have tested true, resulting in the variable `i` being unassigned.
#### ! (logical negation) expressions
For an expression *expr* of the form `! expr_operand`:
* The definite assignment state of *v* before *expr_operand* is the same as the definite assignment state of *v* before *expr*.
* The definite assignment state of *v* after *expr* is determined by:
* If the state of *v* after *expr_operand *is definitely assigned, then the state of *v* after *expr* is definitely assigned.
* If the state of *v* after *expr_operand *is not definitely assigned, then the state of *v* after *expr* is not definitely assigned.
* If the state of *v* after *expr_operand *is "definitely assigned after false expression", then the state of *v* after *expr* is "definitely assigned after true expression".
* If the state of *v* after *expr_operand *is "definitely assigned after true expression", then the state of *v* after *expr* is "definitely assigned after false expression".
#### ?? (null coalescing) expressions
For an expression *expr* of the form `expr_first ?? expr_second`:
* The definite assignment state of *v* before *expr_first* is the same as the definite assignment state of *v* before *expr*.
* The definite assignment state of *v* before *expr_second* is the same as the definite assignment state of *v* after *expr_first*.
* The definite assignment statement of *v* after *expr* is determined by:
2019-04-13 20:44:05 +02:00
* If *expr_first* is a constant expression ([Constant expressions](expressions.md#constant-expressions)) with value null, then the state of *v* after *expr* is the same as the state of *v* after *expr_second*.
* Otherwise, the state of *v* after *expr* is the same as the definite assignment state of *v* after *expr_first*.
#### ?: (conditional) expressions
For an expression *expr* of the form `expr_cond ? expr_true : expr_false`:
* The definite assignment state of *v* before *expr_cond* is the same as the state of *v* before *expr*.
* The definite assignment state of *v* before *expr_true* is definitely assigned if and only if one of the following holds:
* *expr_cond* is a constant expression with the value `false`
* the state of *v* after *expr_cond* is definitely assigned or "definitely assigned after true expression".
* The definite assignment state of *v* before *expr_false* is definitely assigned if and only if one of the following holds:
* *expr_cond* is a constant expression with the value `true`
2021-04-15 09:52:19 +02:00
* the state of *v* after *expr_cond* is definitely assigned or "definitely assigned after false expression".
* The definite assignment state of *v* after *expr* is determined by:
* If *expr_cond* is a constant expression ([Constant expressions](expressions.md#constant-expressions)) with value `true` then the state of *v* after *expr* is the same as the state of *v* after *expr_true*.
* Otherwise, if *expr_cond* is a constant expression ([Constant expressions](expressions.md#constant-expressions)) with value `false` then the state of *v* after *expr* is the same as the state of *v* after *expr_false*.
* Otherwise, if the state of *v* after *expr_true* is definitely assigned and the state of *v* after *expr_false* is definitely assigned, then the state of *v* after *expr* is definitely assigned.
* Otherwise, the state of *v* after *expr* is not definitely assigned.
#### Anonymous functions
For a *lambda_expression* or *anonymous_method_expression* *expr* with a body (either *block* or *expression*) *body*:
* The definite assignment state of an outer variable *v* before *body* is the same as the state of *v* before *expr*. That is, definite assignment state of outer variables is inherited from the context of the anonymous function.
* The definite assignment state of an outer variable *v* after *expr* is the same as the state of *v* before *expr*.
The example
```csharp
delegate bool Filter(int i);
void F() {
int max;
// Error, max is not definitely assigned
Filter f = (int n) => n < max;
max = 5;
DoWork(f);
}
```
generates a compile-time error since `max` is not definitely assigned where the anonymous function is declared. The example
```csharp
delegate void D();
void F() {
int n;
D d = () => { n = 1; };
d();
// Error, n is not definitely assigned
Console.WriteLine(n);
}
```
also generates a compile-time error since the assignment to `n` in the anonymous function has no affect on the definite assignment state of `n` outside the anonymous function.
## Variable references
A *variable_reference* is an *expression* that is classified as a variable. A *variable_reference* denotes a storage location that can be accessed both to fetch the current value and to store a new value.
```antlr
variable_reference
: expression
;
```
In C and C++, a *variable_reference* is known as an *lvalue*.
## Atomicity of variable references
Reads and writes of the following data types are atomic: `bool`, `char`, `byte`, `sbyte`, `short`, `ushort`, `uint`, `int`, `float`, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list are also atomic. Reads and writes of other types, including `long`, `ulong`, `double`, and `decimal`, as well as user-defined types, are not guaranteed to be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.