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
2 changes: 1 addition & 1 deletion standard/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@
> P p2 = delegate { return; }; // Error, return type mismatch
> P p3 = delegate { return 1; }; // Ok
> P p4 = delegate { return "Hello"; }; // Error, return type mismatch
> P p5 = delegate(int[] a) // Ok

Check warning on line 794 in standard/conversions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/conversions.md#L794

MDC032::Line length 82 > maximum 81
> {
> return a[0];
> };
Expand Down Expand Up @@ -853,7 +853,7 @@

### 10.7.2 Evaluation of anonymous function conversions to delegate types

Conversion of an anonymous function to a delegate type produces a delegate instance that references the anonymous function and the (possibly empty) set of captured outer variables that are active at the time of the evaluation. When the delegate is invoked, the body of the anonymous function is executed. The code in the body is executed using the set of captured outer variables referenced by the delegate. A *delegate_creation_expression* ([§12.8.17.5](expressions.md#128175-delegate-creation-expressions)) can be used as an alternate syntax for converting an anonymous method to a delegate type.
Conversion of an anonymous function to a delegate type produces a delegate instance that references the anonymous function and, for non-`static` anonymous functions, the (possibly empty) set of captured outer variables that are active at the time of the evaluation. When the delegate is invoked, the body of the anonymous function is executed. The code in the body is executed using the set of captured outer variables referenced by the delegate. A *delegate_creation_expression* ([§12.8.17.5](expressions.md#128175-delegate-creation-expressions)) can be used as an alternate syntax for converting an anonymous method to a delegate type.

The invocation list of a delegate produced from an anonymous function contains a single entry. The exact target object and target method of the delegate are unspecified. In particular, it is unspecified whether the target object of the delegate is `null`, the `this` value of the enclosing function member, or some other object.

Expand Down
44 changes: 33 additions & 11 deletions standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2177,8 +2177,8 @@
A *null_conditional_invocation_expression* is syntactically either a *null_conditional_member_access* ([§12.8.8](expressions.md#1288-null-conditional-member-access)) or *null_conditional_element_access* ([§12.8.13](expressions.md#12813-null-conditional-element-access)) where the final *dependent_access* is an invocation expression ([§12.8.10](expressions.md#12810-invocation-expressions)).

A *null_conditional_invocation_expression* occurs within the context of a *statement_expression* ([§13.7](statements.md#137-expression-statements)), *anonymous_function_body* ([§12.21.1](expressions.md#12211-general)), or *method_body* ([§15.6.1](classes.md#1561-general)).

Check warning on line 2180 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L2180

MDC032::Line length 84 > maximum 81
Unlike the syntactically equivalent *null_conditional_member_access* or *null_conditional_element_access*, a *null_conditional_invocation_expression* may be classified as nothing.

Check warning on line 2181 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L2181

MDC032::Line length 85 > maximum 81

```ANTLR
null_conditional_invocation_expression
Expand Down Expand Up @@ -2258,7 +2258,7 @@
- The *primary_expression* has compile-time type `dynamic`.
- At least one expression of the *argument_list* has compile-time type `dynamic`.

In this case the compile-time type of the *element_access* depends on the compile-time type of its *primary_expression*: if it has an array type then the compile-time type is the element type of that array type; otherwise the compile-time type is `dynamic` and the *element_access* is classified as a value of type `dynamic`. The rules below to determine the meaning of the *element_access* are then applied at run-time, using the run-time type instead of the compile-time type of those of the *primary_expression* and *argument_list* expressions which have the compile-time type `dynamic`. If the *primary_expression* does not have compile-time type `dynamic`, then the element access undergoes a limited compile-time check as described in [§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation).

Check warning on line 2261 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L2261

MDC032::Line length 82 > maximum 81

> *Example*:
>
Expand Down Expand Up @@ -3396,7 +3396,7 @@

### 12.8.22 Stack allocation

A stack allocation expression allocates a block of memory from the execution stack. The ***execution stack*** is an area of memory where local variables are stored. The execution stack is not part of the managed heap. The memory used for local variable storage is automatically recovered when the current function returns.

Check warning on line 3399 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L3399

MDC032::Line length 86 > maximum 81

The safe context rules for a stack allocation expression are described in [§16.4.15.7](structs.md#164157-stackalloc).

Expand Down Expand Up @@ -5256,18 +5256,23 @@
## 12.21 Anonymous function expressions

### 12.21.1 General

Check warning on line 5259 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L5259

MDC032::Line length 92 > maximum 81
An ***anonymous function*** is an expression that represents an “in-line” method definition. An anonymous function does not have a value or type in and of itself, but is convertible to a compatible delegate or expression-tree type. The evaluation of an anonymous-function conversion depends on the target type of the conversion: If it is a delegate type, the conversion evaluates to a delegate value referencing the method that the anonymous function defines. If it is an expression-tree type, the conversion evaluates to an expression tree that represents the structure of the method as an object structure.

> *Note*: For historical reasons, there are two syntactic flavors of anonymous functions, namely *lambda_expression*s and *anonymous_method_expression*s. For almost all purposes, *lambda_expression*s are more concise and expressive than *anonymous_method_expression*s, which remain in the language for backwards compatibility. *end note*

Check warning on line 5263 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L5263

MDC032::Line length 90 > maximum 81
```ANTLR
lambda_expression
: 'async'? anonymous_function_signature '=>' anonymous_function_body
: anonymous_function_modifier? anonymous_function_signature '=>' anonymous_function_body
;

anonymous_method_expression
: 'async'? 'delegate' explicit_anonymous_function_signature? block
: anonymous_function_modifier? 'delegate' explicit_anonymous_function_signature? block
;

anonymous_function_modifier
: 'async' 'static'?
| 'static' 'async'?
;

anonymous_function_signature
Expand Down Expand Up @@ -5316,9 +5321,18 @@
;
```

When recognising an *anonymous_function_body* if both the *null_conditional_invocation_expression* and *expression* alternatives are applicable then the former shall be chosen.
If the modifier `static` is present, the anonymous function cannot capture state from the enclosing scope. A `static` anonymous function may reference `static` members, type parameters, and constant definitions from the enclosing scope.

A non-`static` local function or non-`static` anonymous function can capture state from an enclosing `static` anonymous function, but cannot capture state outside the enclosing `static` anonymous function.

<!-- markdownlint-disable MD028 -->
> *Note*: Removing the `static` modifier from an anonymous function in a valid program does not change the meaning of the program, other than possibly affecting delegate instance identity (§10.7.2). *end note*

> *Note*: A `static` anonymous function is not required to produce the same delegate instance on each evaluation. See §10.7.2. *end note*
<!-- markdownlint-enable MD028 -->

When recognising an *anonymous_function_body* if both the *null_conditional_invocation_expression* and *expression* alternatives are applicable then the former shall be chosen.

> *Note*: The overlapping of, and priority between, alternatives here is solely for descriptive convenience; the grammar rules could be elaborated to remove the overlap. ANTLR, and other grammar systems, adopt the same convenience and so *anonymous_function_body* has the specified semantics automatically. *end note*
<!-- markdownlint-disable MD028 -->

Expand Down Expand Up @@ -5359,17 +5373,20 @@

A *block* body of an anonymous function is always reachable ([§13.2](statements.md#132-end-points-and-reachability)).

> *Example*: Some examples of anonymous functions follow below:

Check warning on line 5376 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L5376

MDC032::Line length 102 > maximum 81
>

Check warning on line 5377 in standard/expressions.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/expressions.md#L5377

MDC032::Line length 97 > maximum 81
> ```csharp
> x => x + 1 // Implicitly typed, expression body
> x => { return x + 1; } // Implicitly typed, block body
> (int x) => x + 1 // Explicitly typed, expression body
> (int x) => { return x + 1; } // Explicitly typed, block body
> static (int x) => x + 1 // static anonymous function, explicitly typed, expression body
> static (int x) => { return x + 1; } // static anonymous function, explicitly typed, block body
> (x, y) => x * y // Multiple parameters
> () => Console.WriteLine() // No parameters
> async (t1,t2) => await t1 + await t2 // Async
> delegate (int x) { return x + 1; } // Anonymous method expression
> delegate (int x) { return x + 1; } // Anonymous method expression
> static delegate (int x) { return x + 1; } // static anonymous method expression
> delegate { return 1 + 1; } // Parameter list omitted
> ```
>
Expand Down Expand Up @@ -5399,11 +5416,14 @@
- If the anonymous function includes a signature, the parameters specified in the signature are available in the body. If the anonymous function has no signature it can be converted to a delegate type or expression type having parameters ([§10.7](conversions.md#107-anonymous-function-conversions)), but the parameters cannot be accessed in the body.
- Except for by-reference parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a by-reference parameter.
- Except for parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a parameter of a `ref struct` type.
- When the type of `this` is a struct type, it is a compile-time error for the body to access `this`. This is true whether the access is explicit (as in `this.x`) or implicit (as in `x` where `x` is an instance member of the struct). This rule simply prohibits such access and does not affect whether member lookup results in a member of the struct.
- The body has access to the outer variables ([§12.21.6](expressions.md#12216-outer-variables)) of the anonymous function. Access of an outer variable will reference the instance of the variable that is active at the time the *lambda_expression* or *anonymous_method_expression* is evaluated ([§12.21.7](expressions.md#12217-evaluation-of-anonymous-function-expressions)).
- If the modifier `static` is present, it is a compile-time error for the body to reference `this`, `base`, or any outer variable, except as an operand of a `nameof` expression.
- If the modifier `static` is absent, when the type of `this` is a struct type, it is a compile-time error for the body to access `this`. This is true whether the access is explicit (as in `this.x`) or implicit (as in `x` where `x` is an instance member of the struct). This rule only prohibits such access and does not affect whether member lookup results in a member of the struct.
- If the modifier `static` is absent, the body has access to the outer variables ([§12.21.6](expressions.md#12216-outer-variables)) of the anonymous function. Access of an outer variable will reference the instance of the variable that is active at the time the *lambda_expression* or *anonymous_method_expression* is evaluated ([§12.21.7](expressions.md#12217-evaluation-of-anonymous-function-expressions)).
- It is a compile-time error for the body to contain a `goto` statement, a `break` statement, or a `continue` statement whose target is outside the body or within the body of a contained anonymous function.
- A `return` statement in the body returns control from an invocation of the nearest enclosing anonymous function, not from the enclosing function member.

> *Note*: The `static` modifier on an anonymous function does not change accessibility rules. Private members of the enclosing scope remain accessible. *end note*

It is explicitly unspecified whether there is any way to execute the block of an anonymous function other than through evaluation and invocation of the *lambda_expression* or *anonymous_method_expression*. In particular, a compiler may choose to implement an anonymous function by synthesizing one or more named methods or types. The names of any such synthesized elements shall be of a form reserved for compiler use ([§6.4.3](lexical-structure.md#643-identifiers)).

### 12.21.4 Overload resolution
Expand Down Expand Up @@ -5483,7 +5503,9 @@

#### 12.21.6.1 General

Any local variable, value parameter, or parameter array whose scope includes the *lambda_expression* or *anonymous_method_expression* is called an ***outer variable*** of the anonymous function. In an instance function member of a class, the this value is considered a value parameter and is an outer variable of any anonymous function contained within the function member.
Any local variable, value parameter, or parameter array whose scope includes the *lambda_expression* or *anonymous_method_expression* is called an ***outer variable*** of the anonymous function. In an instance function member of a class, the `this` value is considered a value parameter and is an outer variable of any anonymous function contained within the function member.

If the modifier `static` is present, the anonymous function cannot capture state from the enclosing scope. As a result, locals, parameters, and `this` from the enclosing scope are not available to that anonymous function.

#### 12.21.6.2 Captured outer variables

Expand Down Expand Up @@ -5524,7 +5546,7 @@
>
> *end example*

When a local variable or a value parameter is captured by an anonymous function, the local variable or parameter is no longer considered to be a fixed variable ([§24.4](unsafe-code.md#244-fixed-and-moveable-variables)), but is instead considered to be a moveable variable. However, captured outer variables cannot be used in a `fixed` statement ([§24.7](unsafe-code.md#247-the-fixed-statement)), so the address of a captured outer variable cannot be taken.
When a local variable or a value parameter is captured by a non-`static` anonymous function, the local variable or parameter is no longer considered to be a fixed variable ([§24.4](unsafe-code.md#244-fixed-and-moveable-variables)), but is instead considered to be a moveable variable. However, captured outer variables cannot be used in a `fixed` statement ([§24.7](unsafe-code.md#247-the-fixed-statement)), so the address of a captured outer variable cannot be taken.

> *Note*: Unlike an uncaptured variable, a captured local variable can be simultaneously exposed to multiple threads of execution. *end note*

Expand Down Expand Up @@ -5563,7 +5585,7 @@
>
> *end example*

When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantiation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantiation to simply use the same storage location. However, when a non-`static` anonymous function captures a local variable, the effects of instantiation become apparent.

> *Example*: The example
>
Expand Down Expand Up @@ -5683,7 +5705,7 @@
>
> *end example*

It is possible for anonymous function delegates to share some captured variables yet have separate instances of others.
It is possible for non-`static` anonymous function delegates to share some captured variables yet have separate instances of others.

> *Example*: For example, if `F` is changed to
>
Expand Down Expand Up @@ -5712,7 +5734,7 @@
>
> *end example*

Separate anonymous functions can capture the same instance of an outer variable.
Separate non-`static` anonymous functions can capture the same instance of an outer variable.

> *Example*: In the example:
>
Expand Down
2 changes: 1 addition & 1 deletion standard/unsafe-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ The `&` operator ([§24.6.5](unsafe-code.md#2465-the-address-of-operator)) permi

In precise terms, a fixed variable is one of the following:

- A variable resulting from a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) that refers to a local variable, value parameter, or parameter array, unless the variable is captured by an anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)).
- A variable resulting from a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) that refers to a local variable, value parameter, or parameter array, unless the variable is captured by a non-`static` anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)).
- A variable resulting from a *member_access* ([§12.8.7](expressions.md#1287-member-access)) of the form `V.I`, where `V` is a fixed variable of a *struct_type*.
- A variable resulting from a *pointer_indirection_expression* ([§24.6.2](unsafe-code.md#2462-pointer-indirection)) of the form `*P`, a *pointer_member_access* ([§24.6.3](unsafe-code.md#2463-pointer-member-access)) of the form `P->I`, or a *pointer_element_access* ([§24.6.4](unsafe-code.md#2464-pointer-element-access)) of the form `P[E]`.

Expand Down
4 changes: 2 additions & 2 deletions standard/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ For the purpose of definite-assignment checking, an array element is considered

### 9.2.5 Value parameters

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 when execution of the function body completes. However, if the value parameter is captured by an anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from that anonymous function is eligible for garbage collection.
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 when execution of the function body completes. However, if the value parameter is captured by a non-`static` anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)), its lifetime 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.

Expand Down Expand Up @@ -124,7 +124,7 @@ A ***local variable*** is declared by a *local_variable_declaration*, *declarati

A *local_variable_declaration* can occur in a *block*, a *for_statement*, a *switch_block*, or a *using_statement*. A *declaration_expression* can occur as an `out` *argument_value*, and as a *tuple_element* that is the target of a deconstructing assignment ([§12.23.2](expressions.md#12232-simple-assignment)).

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 from entry into the scope with which it is associated, at least until execution of that scope ends in some way. (Entering an enclosed *block*, calling a method, or yielding a value from an iterator block suspends, but does not end, execution of the current scope.) If the local variable is captured by an anonymous function ([§12.21.6.2](expressions.md#122162-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 scope is entered recursively or iteratively, a new instance of the local variable is created each time, and its initializer, if any, is evaluated each time.
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 from entry into the scope with which it is associated, at least until execution of that scope ends in some way. (Entering an enclosed *block*, calling a method, or yielding a value from an iterator block suspends, but does not end, execution of the current scope.) If the local variable is captured by a non-`static` anonymous function ([§12.21.6.2](expressions.md#122162-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 scope is entered recursively or iteratively, a new instance of the local variable is created each time, and its initializer, if any, is evaluated each time.

> *Note*: A local variable is instantiated each time its scope is entered. This behavior is visible to user code containing anonymous methods. *end note*
<!-- markdownlint-disable MD028 -->
Expand Down
Loading