Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6722,7 +6722,7 @@ If the input can be syntactically recognised as both a *deconstructing_assignmen

> *Note*: ANTLR grammar semantics enforce this requirement due to the ordering of the alternatives. *Semantically* there is no overlap between the four alternatives, this is a syntactic disambiguation.

The *simple_assignment* and *compound_assignment* expressions assign a new value to a variable, a property, or an indexer element. Event assignment ([§12.23.6](expressions.md#12236-event-assignment)), a subset of *compound_assignment*, assigns a new value to an event. The *ref_assignment* expression assigns a variable reference ([§9.5](variables.md#95-variable-references)) to a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)). The *deconstructing_assignment* assigns values to two or more targets.
The *simple_assignment* and *compound_assignment* expressions assign a new value to a variable, a property, or an indexer element. Event assignment ([§12.23.6](expressions.md#12236-event-assignment)), a subset of *compound_assignment*, assigns a new value to an event. The *ref_assignment* expression assigns a variable reference ([§9.5](variables.md#95-variable-references)) to a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)). The *deconstructing_assignment* assigns values to the non-discard targets of a *deconstructor*, which has two or more *deconstructor_element*s.

The target of a *simple_assignment*, *ref_assignment*, or any target of a *deconstructing_assignment* may be a discard ([§9.2.9.2](variables.md#9292-discards)). The left operand of a *compound_assignment* shall not be a discard. When the left operand of an assignment is a discard, the corresponding right-side expression is evaluated but no value is stored.

Expand Down Expand Up @@ -6916,6 +6916,7 @@ It is a compile time error if any *variable_reference*, including any reclassifi
-->
There are restrictions on which *deconstructor_element*s are valid in a given context which are not expressed in the grammar:

- a simple discard (a *discard_token* that is *not* reclassified as a *variable_reference*, see above) is treated as neither a *declaration_expression* nor a *variable_reference*; it may occur as a *deconstructor_element* in any context and does not affect the restrictions applied to the other elements;
- a *declaration_expression* can only occur if the containing *deconstructor* is at the start of a *statement* or a member of a *for_initializer*; and
- a *variable_reference* can only occur if the containing *deconstructor*:
- **is not** at the start of a statement, or
Expand Down Expand Up @@ -6951,6 +6952,19 @@ The run-time processing of a deconstructing assignment, now `d = e`, proceeds as

<!-- markdownlint-enable MD028 -->
> *Note*: The construction of intermediate tuples produced by this algorithm might be elided by an implementation as specified by [§8.3.11.2](types.md#83112-eliding-intermediate-tuple-creation). *end note*
<!-- markdownlint-disable MD028 -->

<!-- markdownlint-enable MD028 -->
> *Example*: A discard is a placeholder rather than a *variable_reference*, so it can be combined with an existing variable on the left side of a deconstructing assignment:
>
> <!-- Example: {template:"standalone-console", name:"DeconstructingAssignmentDiscard1", expectedOutput:["1"]} -->
> ```csharp
> int x = 0;
> (x, _) = (1, 2); // assigns 1 to the existing variable x; the discard ignores 2
> Console.WriteLine(x);
> ```
>
> *end example*

#### 12.23.3.2 Abridged deconstructors

Expand Down
25 changes: 19 additions & 6 deletions standard/statements.md
Original file line number Diff line number Diff line change
Expand Up @@ -1441,9 +1441,9 @@ The local variable `d` is not visible to or accessible to any user code. In par

#### 13.9.5.4 Deconstructing foreach

A deconstructing foreach replaces the declaration and initialisation of a single iteration variable per iteration, in the synchronous and asynchronous foreach statements, with a collection of zero or more iteration variables declared per iteration within the *deconstructor*([§12.23.3](expressions.md#12233-deconstructing-assignment)) of a *deconstructing_assignment*.
A deconstructing foreach replaces the declaration and initialisation of a single iteration variable per iteration, in the synchronous and asynchronous foreach statements, with a *deconstructor* ([§12.23.3](expressions.md#12233-deconstructing-assignment)) whose *deconstructor_element*s are matched, per iteration, against the elements obtained by deconstructing each collection element.

All variables assigned to by the *deconstructor* must be declared within the *deconstructor*, it is a compile time error for any *deconstructor_element* to be a *variable_reference*.
Each *deconstructor_element* shall be either a *declaration_expression*, which declares an iteration variable, or a discard, which is a placeholder that matches its corresponding element without declaring a variable. It is a compile-time error for any *deconstructor_element* to be a *variable_reference*.

A foreach statement of the form:

Expand Down Expand Up @@ -1471,10 +1471,10 @@ is semantically equivalent to:
}
```

This follows the behavior of synchronous foreach ([§13.9.5.2](statements.md#13952-synchronous-foreach)), differing by replacing the delaration and initialisation of a single iteration variable with a *deconstructing_assignment* which declares and assigns zero or more initialisation variables:
This follows the behavior of synchronous foreach ([§13.9.5.2](statements.md#13952-synchronous-foreach)), differing by replacing the declaration and initialisation of a single iteration variable with a *deconstructing_assignment* whose *deconstructor* declares an iteration variable for each *declaration_expression* it contains (discards declare none):

- `C` and `E` are determined as for synchronous foreach
- `e` is not visible or accessible anywhere in the program accept as indicated in the above code
- `e` is not visible or accessible anywhere in the program except as indicated in the above code
- the variables declared by the «deconstructor» are read-only to the «embedded_statement»
- the code in the `finally` block is determined as for synchronous foreach

Expand Down Expand Up @@ -1504,12 +1504,25 @@ is semantically equivalent to:
}
```

This follows the behavior of asynchronous foreach ([§13.9.5.3](statements.md#13953-asynchronous-foreach)), differing by replacing the delaration and initialisation of a single iteration variable with a *deconstructing_assignment* which declares and assigns zero or more initialisation variables:
This follows the behavior of asynchronous foreach ([§13.9.5.3](statements.md#13953-asynchronous-foreach)), differing by replacing the declaration and initialisation of a single iteration variable with a *deconstructing_assignment* whose *deconstructor* declares an iteration variable for each *declaration_expression* it contains (discards declare none):

- `enumerator` is not visible or accessible anywhere in the program accept as indicated in the above code
- `enumerator` is not visible or accessible anywhere in the program except as indicated in the above code
- the variables declared by the «deconstructor» are read-only to the «embedded_statement»
- the code in the `finally` block is determined as for asynchronous foreach

> *Example*: A deconstructing foreach uses discards as placeholders for elements that are not needed. Here each element of the collection is a tuple whose second element is discarded:
>
> <!-- Example: {template:"standalone-console", name:"DeconstructingForeach1", expectedOutput:["1","3"]} -->
> ```csharp
> var points = new List<(int X, int Y)> { (1, 2), (3, 4) };
> foreach (var (x, _) in points)
> {
> Console.WriteLine(x);
> }
> ```
>
> *end example*

## 13.10 Jump statements

### 13.10.1 General
Expand Down
Loading