Skip to content

Commit fba28ab

Browse files
committed
specification for null-conditional-operator
Add the sections and grammar for the `?.` null conditional operator.
1 parent 67610fa commit fba28ab

File tree

2 files changed

+110
-4
lines changed

2 files changed

+110
-4
lines changed

standard/expressions.md

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,6 +2070,7 @@ member_declarator
20702070
: simple_name
20712071
| member_access
20722072
| base_access
2073+
| null_conditional_member_access
20732074
| identifier '=' expression
20742075
;
20752076
```
@@ -2118,7 +2119,7 @@ Within the same program, two anonymous object initializers that specify a sequen
21182119
21192120
The `Equals` and `GetHashcode` methods on anonymous types override the methods inherited from `object`, and are defined in terms of the `Equals` and `GetHashcode` of the properties, so that two instances of the same anonymous type are equal if and only if all their properties are equal.
21202121
2121-
A member declarator can be abbreviated to a simple name ([§12.7.3](expressions.md#1273-simple-names)), a member access ([§12.7.5](expressions.md#1275-member-access)) or a base access ([§12.7.9](expressions.md#1279-base-access)). This is called a ***projection initializer*** and is shorthand for a declaration of and assignment to a property with the same name. Specifically, member declarators of the forms
2122+
A member declarator can be abbreviated to a simple name ([§12.7.3](expressions.md#1273-simple-names)), a member access ([§12.7.5](expressions.md#1275-member-access)), a base access ([§12.7.9](expressions.md#1279-base-access)), or a null-conditional member accessnull-conditional-operator-initializer). This is called a ***projection initializer*** and is shorthand for a declaration of and assignment to a property with the same name. Specifically, member declarators of the forms
21222123
21232124
identifier»` andexpr» . «identifier»`
21242125
@@ -2380,6 +2381,7 @@ The `+`, `-`, `!`, `~`, `++`, `--`, cast, and `await` operators are called the u
23802381
```ANTLR
23812382
unary_expression
23822383
: primary_expression
2384+
| null_conditional_expression
23832385
| '+' unary_expression
23842386
| '-' unary_expression
23852387
| '!' unary_expression
@@ -2393,6 +2395,106 @@ unary_expression
23932395

23942396
If the operand of a *unary_expression* has the compile-time type `dynamic`, it is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the *unary_expression* is `dynamic`, and the resolution described below will take place at run-time using the run-time type of the operand.
23952397

2398+
### §null-conditional-operator Null-conditional operator
2399+
2400+
#### §null-conditional-operator-general General
2401+
2402+
The null-conditional operator applies a list of operations to its operand only if that operand is non-`null`. Otherwise the result of applying the operator is `null`.
2403+
2404+
```antlr
2405+
null_conditional_expression
2406+
: primary_expression null_conditional_operations
2407+
;
2408+
2409+
null_conditional_operations
2410+
: null_conditional_operations? '?' '.' identifier type_argument_list?
2411+
| null_conditional_operations? '?' '[' argument_list ']'
2412+
| null_conditional_operations '.' identifier type_argument_list?
2413+
| null_conditional_operations '[' argument_list ']'
2414+
| null_conditional_operations '(' argument_list? ')'
2415+
;
2416+
```
2417+
2418+
The list of operations can include member access and element access operations (which may themselves be null-conditional), as well as invocation.
2419+
> *Example* : The expression `a.b?[0]?.c()` is a *null_conditional_expression* with a *primary_expression* `a.b` and *null_conditional_operations* `?[0]` (null-conditional element access), `?.c` (null-conditional member access) and `()` (invocation). *end example*
2420+
2421+
For a *null_conditional_expression* `E` with a *primary_expression* `P`, let `E₀` be the expression obtained by textually removing the leading `?` from each of the *null_conditional_operations* of `E` that have one. Conceptually, `E₀` is the expression that will be evaluated if none of the null checks represented by the `?`s do find a `null`.
2422+
2423+
Also, let `E₁` be the expression obtained by textually removing the leading `?` from just the first of the *null_conditional_operations* in `E`. This may lead to a *primary-expression* (if there was just one `?`) or to another *null_conditional_expression*.
2424+
2425+
> *Example* : If `E` is the expression `a.b?[0]?.c()`, then `E₀` is the expression `a.b[0].c()` and `E₁` is the expression `a.b[0]?.c()`. *end example*
2426+
2427+
If `E₀` is classified as nothing, then `E` is classified as nothing. Otherwise `E` is classified as a value.
2428+
`E₀` and `E₁` are used to determine the meaning of `E`:
2429+
- If `E` occurs as a *statement_expression* the meaning of `E` is the same as the statement
2430+
```csharp
2431+
if ((object)P != null) E₁;
2432+
```
2433+
except that `P` is evaluated only once.
2434+
- Otherwise, if `E₀` is classified as nothing a compile-time error occurs.
2435+
- Otherwise, let `T₀` be the type of `E₀`.
2436+
- If `T₀` is a type parameter that is not known to be a reference type or a non-nullable value type, a compile-time error occurs.
2437+
- If `T₀` is a non-nullable value type, then the type of `E` is `T₀?`, and the meaning of `E` is the same as
2438+
```csharp
2439+
((object)P == null) ? (T?)null : E
2440+
```
2441+
except that `P` is evaluated only once.
2442+
- Otherwise the type of `E` is `T₀`, and the meaning of `E` is the same as
2443+
```csharp
2444+
((object)P == null) ? null : E
2445+
```
2446+
except that `P` is evaluated only once.
2447+
2448+
If `E₁` is itself a *null_conditional_expression*, then these rules are applied again, nesting the tests for `null` until there are no further `?`'s, and the expression has been reduced all the way down to the primary-expression `E₀`.
2449+
2450+
> *Example* : If the expression `a.b?[0]?.c()` occurs as a statement-expression, as in the statement:
2451+
> ```csharp
2452+
> a.b?[0]?.c();
2453+
> ```
2454+
> its meaning is equivalent to:
2455+
> ```csharp
2456+
> if (a.b != null) a.b[0]?.c();
2457+
> ```
2458+
> which again is equivalent to:
2459+
> ```csharp
2460+
> if (a.b != null) if (a.b[0] != null) a.b[0].c();
2461+
> ```
2462+
> except that `a.b` and `a.b[0]` are evaluated only once.
2463+
> If it occurs in a context where its value is used, as in:
2464+
> ```csharp
2465+
> var x = a.b?[0]?.c();
2466+
> ```
2467+
> and assuming that the type of the final invocation is not a non-nullable value type, its meaning is equivalent to:
2468+
> ```csharp
2469+
> var x = (a.b == null) ? null : (a.b[0] == null) ? null : a.b[0].c();
2470+
> ```
2471+
> except that `a.b` and `a.b[0]` are evaluated only once. *end example*
2472+
2473+
#### §null-conditional-operator-initializer Null-conditional expressions as projection initializers
2474+
2475+
A null-conditional expression is only allowed as a *member_declarator* in an *anonymous_object_creation_expression* ([§12.7.11.7](expressions.md#127117-anonymous-object-creation-expressions)) if it ends with an (optionally null-conditional) member access. Grammatically, this requirement can be expressed as:
2476+
2477+
```antlr
2478+
null_conditional_member_access
2479+
: primary_expression null_conditional_operations? '?' '.' identifier type_argument_list?
2480+
| primary_expression null_conditional_operations '.' identifier type_argument_list?
2481+
;
2482+
```
2483+
2484+
This is a special case of the grammar for *null_conditional_expression* above. The production for *member_declarator* in [§12.7.11.7](expressions.md#127117-anonymous-object-creation-expressions) then includes only *null_conditional_member_access*.
2485+
2486+
#### §null-conditional-operator-statement Null-conditional expressions as statement expressions
2487+
2488+
A null-conditional expression is only allowed as a *statement_expression* ([§13.7](statements.md#137-expression-statements)) if it ends with an invocation. Grammatically, this requirement can be expressed as:
2489+
2490+
```antlr
2491+
null_conditional_invocation_expression
2492+
: primary_expression null_conditional_operations '(' argument_list? ')'
2493+
;
2494+
```
2495+
2496+
This is a special case of the grammar for *null_conditional_expression* above. The production for *statement_expression* in [§13.7](statements.md#137-expression-statements) then includes only *null_conditional_invocation_expression*.
2497+
23962498
### 12.8.2 Unary plus operator
23972499

23982500
For an operation of the form `+x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary plus operators are:
@@ -2556,7 +2658,6 @@ await_expression
25562658
An *await_expression* is only allowed in the body of an async function ([§15.15](classes.md#1515-async-functions)). Within the nearest enclosing async function, an *await_expression* shall not occur in these places:
25572659

25582660
- Inside a nested (non-async) anonymous function
2559-
- In a `catch` or `finally` block of a *try_statement*
25602661
- Inside the block of a *lock_statement*
25612662
- In an anonymous function conversion to an expression tree type ([§11.7.3](conversions.md#1173-evaluation-of-anonymous-function-conversions-to-expression-tree-types))
25622663
- In an unsafe context

standard/statements.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ expression_statement
363363
364364
statement_expression
365365
: invocation_expression
366+
| null_conditional_invocation_expression
366367
| object_creation_expression
367368
| assignment
368369
| post_increment_expression
@@ -1088,13 +1089,17 @@ try_statement
10881089
| 'try' block catch_clause+ finally_clause
10891090
;
10901091
1091-
catch_clauses
1092-
: 'catch' exception_specifier? block
1092+
catch_clause
1093+
: 'catch' exception_specifier? block
10931094
;
10941095
10951096
exception_specifier
10961097
: '(' type identifier? ')'
10971098
;
1099+
1100+
finally_clause
1101+
: 'finally' block
1102+
;
10981103
```
10991104
11001105
There are three possible forms of `try` statements:

0 commit comments

Comments
 (0)