Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nameof expressions alternative #250

Merged
merged 3 commits into from
Apr 7, 2021
Merged
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
85 changes: 84 additions & 1 deletion standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,7 @@ primary_no_array_creation_expression
| checked_expression
| unchecked_expression
| default_value_expression
| nameof_expression
| anonymous_method_expression
;
```
Expand Down Expand Up @@ -2314,7 +2315,88 @@ If the *type* in a *default_value_expression* evaluates at run-time to a referen

A *default_value_expression* is a constant expression ([§12.20](expressions.md#1220-constant-expressions)) if *type* is a reference type or a type parameter that is known to be a reference type ([§9.2](types.md#92-reference-types)). In addition, a *default_value_expression* is a constant expression if the type is one of the following value types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,` or any enumeration type.

### 12.7.16 Anonymous method expressions
### §expressions-nameof-expressions Nameof expressions

A *nameof_expression* is used to obtain the name of a program entity as a constant string.

```antlr
nameof_expression
: 'nameof' '(' named_entity ')'
;

named_entity
: simple_name
| named_entity_target '.' identifier type_argument_list?
;

named_entity_target
: 'this'
| 'base'
| named_entity
| predefined_type
| qualified_alias_member
;
```

Because `nameof` is not a keyword, a *nameof_expression* is always syntactically ambiguous with an invocation of the simple name `nameof`. For compatibility reasons, if a name lookup ([§12.7.3](expressions.md#simple-names)) of the name `nameof` succeeds, the expression is treated as an *invocation_expression* -- regardless of whether the invocation is valid. Otherwise it is a *nameof_expression*.

Simple name and member access lookups are performed on the *named_entity* at compile time, following the rules described in [§12.7.3](expressions.md#1273-simple-names) and [§12.7.5](expressions.md#1275-member-access). However, where the lookup described in [§12.7.3](expressions.md#1273-simple-names) and [§12.7.5](expressions.md#1275-member-access) results in an error because an instance member was found in a static context, a *nameof_expression* produces no such error.

It is a compile-time error for a *named_entity* designating a method group to have a *type_argument_list*. It is a compile time error for a *named_entity_target* to have the type `dynamic`.

A *nameof_expression* is a constant expression of type `string`, and has no effect at runtime. Specifically, its *named_entity* is not evaluated, and is ignored for the purposes of definite assignment analysis ([§10.4.4.22](variables.md#104422-general-rules-for-simple-expressions)). Its value is the last identifier of the *named_entity* before the optional final *type_argument_list*, transformed in the following way:

- The prefix "`@`", if used, is removed.
- Each *unicode_escape_sequence* is transformed into its corresponding Unicode character.
- Any *formatting_characters* are removed.

These are the same transformations applied in [§7.4.3](lexical-structure.md#743-identifiers) when testing equality between identifiers.

> *Example*: The following illustrates the results of various `nameof` expressions, assuming a generic type `List<T>` declared within the `System.Collections.Generic` namespace:
> ```csharp
> using System.Collections.Generic;
>
> using TestAlias = System.String;
>
> class Program {
> static void Main() {
> var point = (x: 3, y: 4);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tuple types are not part of C# 6.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted, thanks! I'll leave this in right now, but before merging I'll add a commit to remove it, then create a PR to re-add it for C# 7. (We should potentially show the use of point.Item1 as well.)

>
> string n1 = nameof(System); // "System"
> string n2 = nameof(System.Collections.Generic); // "Generic"
> string n3 = nameof(point); // "point"
> string n4 = nameof(point.x); // "x"
> string n5 = nameof(Program); // "Program"
> string n6 = nameof(System.Int32); // "Int32"
> string n7 = nameof(TestAlias); // "TestAlias"
> string n8 = nameof(List<int>); // "List"
> string n9 = nameof(Program.InstanceMethod); // "InstanceMethod"
> string n10 = nameof(Program.GenericMethod); // "GenericMethod"
> string n11 = nameof(Program.NestedClass); // "NestedClass"
>
> // Invalid
> // string x1 = nameof(List<>); // Empty type argument list
> // string x2 = nameof(List<T>); // T is not in scope
> // string x3 = nameof(GenericMethod<>); // Empty type argument list
> // string x4 = nameof(GenericMethod<T>); // T is not in scope
> // string x5 = nameof(GenericMethod<Program>); // Type arguments not permitted for method group
> // string x6 = nameof(int); // Keywords not permitted
> }
>
> void InstanceMethod() { }
>
> void GenericMethod<T>() {
> string n1 = nameof(List<T>); // "List"
> string n2 = nameof(T); // "T"
> }
>
> class NestedClass { }
> }
> ```
> Potentially surprising parts of this example are the resolution of `nameof(System.Collections.Generic)` to just "Generic" instead of the full namespace, and of `nameof(TestAlias)` to "TestAlias" rather than "String".
> *end example*

### 12.7.16 Anonymous method expressions

An *anonymous_method_expression* is one of two ways of defining an anonymous function. These are further described in [§12.16](expressions.md#1216-anonymous-function-expressions).

Expand Down Expand Up @@ -5327,6 +5409,7 @@ Only the following constructs are permitted in constant expressions:
- Parenthesized subexpressions, which are themselves constant expressions.
- Cast expressions.
- `checked` and `unchecked` expressions.
- `nameof` expressions
- The predefined `+`, `–`, `!`, and `~` unary operators.
- The predefined `+`, `–, `*`, `/`, `%`, `<<`, `>>`, `&`, `|`, `^`, `&&`, `||`, `==`, `!=`, `<`, `>`, `<=`, and `>=` binary operators.
- The `?:` conditional operator.
Expand Down
5 changes: 3 additions & 2 deletions standard/lexical-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,9 @@ Contextual_Keyword
: 'add' 'alias' 'ascending' 'async' 'await'
| 'by' 'descending' 'dynamic' 'equals' 'from'
| 'get' 'global' 'group' 'into' 'join'
| 'let' 'orderby' 'partial' 'remove' 'select'
| 'set' 'value' 'var' 'where' 'yield'
| 'let' 'nameof' 'orderby' 'partial' 'remove'
| 'select' 'set' 'value' 'var' 'where'
| 'yield'
;
```

Expand Down
2 changes: 1 addition & 1 deletion standard/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ For all other constant expressions, the definite assignment state of *v* after t

#### 10.4.4.22 General rules for simple expressions

The following rule applies to these kinds of expressions: literals ([§12.7.2](expressions.md#1272-literals)), simple names ([§12.7.3](expressions.md#1273-simple-names)), member access expressions ([§12.7.5](expressions.md#1275-member-access)), non-indexed base access expressions ([§12.7.9](expressions.md#1279-base-access)), `typeof` expressions ([§12.7.12](expressions.md#12712-the-typeof-operator)), and default value expressions ([§12.7.15](expressions.md#12715-default-value-expressions)).
The following rule applies to these kinds of expressions: literals ([§12.7.2](expressions.md#1272-literals)), simple names ([§12.7.3](expressions.md#1273-simple-names)), member access expressions ([§12.7.5](expressions.md#1275-member-access)), non-indexed base access expressions ([§12.7.9](expressions.md#1279-base-access)), `typeof` expressions ([§12.7.12](expressions.md#12712-the-typeof-operator)), default value expressions ([§12.7.15](expressions.md#12715-default-value-expressions)), and `nameof` expressions (§expressions-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.

Expand Down