nameof: proposed design changes

Topics: C# Language Design, VB Language Design
Coordinator
Oct 18, 2014 at 4:37 AM
Edited Oct 19, 2014 at 4:26 AM
We've been reconsidering some details about the "nameof" operator. This was prompted by a few questions:
  • Why can't I write "nameof(this.p)"? My convention is to always put 'this' in front of my fields and properties.
  • Why does the IDE throw an exception when I rename symbols that appear in nameof expressions? Why is it so hard to write analyzers that work correctly in the face of nameof?
A revised spec is below. The chief difference from the original spec (part1 and part2) is that now the argument of nameof(.) is just an expression, like any other expression in the language (in CTP4 it had been an unusual hybrid similar in some ways to the argument of typeof). The common cases of nameof will still be written the same as what's in CTP4, but some edge cases will be different.

There's one chief open design question remaining, detailed below.

Please let us know what you think!

nameof operator: revised spec

The nameof(.) operator takes one expression argument (note that in C#, types are expressions). There are two principles: in essence, (1) the expression must "have a name", and (2) it must resolve to one single symbol.

Bread and butter cases

// Validate parameters 
void f(string s) {
    if (s == null) throw new ArgumentNullException(nameof(s));
}
// MVC Action links
<%= Html.ActionLink("Sign up",
             nameof(UserController),
             nameof(default(UserController).SignUp()))
%>
// INotifyPropertyChanged
int p {
    get { return this._p; }
    set { this._p = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.p)); }
}
// XAML dependency property
public static DependencyProperty AgeProperty = DependencyProperty.Register(nameof(default(C).Age), typeof(int), typeof(C));
// Logging
void f(int i) {
    Log(nameof(f(i)), "method entry");
}
// Attributes
[DebuggerDisplay("={" + nameof(default(C).getString()) + "()}")]
class C {
    string getString() { ... }
}
I'm sorry. Markdown keeps turning my plus symbols into ampersand hash 43. I don't know how to stop it.

I call out the last two cases. If we decided to break principle (2), then we could allow method-groups, and hence omit the argument lists for these two cases. We could reasonably choose either to keep principle (2) or to break it, and there are pros and cons to both sides. More details in the post below.

Implementation

class NameOfExpressionSyntax : ExpressionSyntax { readonly ExpressionSyntax argument; }
For source code that compiles without errors, semanticModel.GetSymbolInfo(argument) will return a single symbol. For erroneous code it might also return 0 or many candidates.

(Under the alternative option, where principle (2) is violated, then we would make GetSymbolInfo return no single preferred symbol, and instead always return a candidate list. Analyzers would now have to deal with this case for successful user code as well as for erroneous user code.)

C# Syntax

expression: … | nameof-expression

nameof-expression:
    nameof ( nameof-expression-argument )

nameof-expression-argument:
    simple-name                  x
    member-access                e.x, e.x<int>, e?.x,
                                 int.TryParse, a::b.c
    invocation-expression        e(args)
    base-access-named            base.x
It is helpful to list what is not allowed as the nameof argument. In general, nameof accepts expressions that have names (including type expressions that have names). All other arguments produce compile-time error "This expression does not have a name". Here are the expressions that are not allowed:
    assignment                   x += 15
    query-expression             from y in z select y
    lambda-expression            () => e
    conditional-expression       a ? b : c
    null-coalescing-expression   a?? b
    binary-expression            ||, &&, |, ^, &, ==, !=,
                                 <, >, <=, >=, is, as, <<,
                                 >>, +, -, *, /, %
    prefix-expression            +, -, !, ~, ++, --,
                                 (cast)e, *, &
    postfix-expression           ++, --
    array-creation-expression    new C[…]
    object-creation-expression   new C(…)
    delegate-creation-expression new Action(…)
    anonymous-object-creation-expression new {…}
    typeof-expression            typeof(int)
    checked-expression           checked(…)
    unchecked-expression         unchecked(…)
    default-value-expression     default(…)
    anonymous-method-expression  delegate {…}
    pointer-member-access        e->x
    sizeof-expression            sizeof(int)
    literal                      "hello", 15
    parenthesized-expression     (x)
    element-access               e[i]
    this-access                  this
    base-access-indexed          base[i]
    await-expression             await e
    nameof-expression            nameof(e)
    vb-dictionary-lookup         e!foo 
Note that there are some types which are not counted as expressions by the C# grammar. These are not allowed as nameof arguments (since the nameof syntax only allows expressions for its argument). It is a pleasant coincidence that none of these types even have names, so they wouldn't be useful as a nameof argument anyway. Even keywords like "int" and "bool" are not names; they are keywords. There's no need to spell out that the following things are not valid expressions, since that's already said by the language syntax, but I'm going to spell it out anyway.
    predefined-type              int, bool, float, object,
                                 dynamic, string, void
    nullable-type                Customer?
    array-type                   Customer[,]
    pointer-type                 Buffer*, void* 
Coordinator
Oct 18, 2014 at 4:39 AM

Semantics

The nameof expression is a constant. In all cases, nameof(…) is evaluated at compile-time to produce a string. Its argument is not evaluated at runtime, and is considered unreachable code (however it does not emit an "unreachable code" warning).

Name lookup. The same rules of "simple name lookup" and "member access" apply to nameof arguments as they do to the rest of the language.

Accessibility. The same rules of accessibility apply to nameof arguments as they do to all other expressions.

Error in binding. The same rules of binding apply to nameof as to all other expressions. If binding to the argument/type would result in an error, e.g. to obsolete methods, then that is still an error for purposes of nameof.

Definite assignment. The same rules of definite assignment apply to nameof arguments as they do to all other unreachable expressions.

VB invocation / indexing. In VB, e(…) might be either an method invocation, a delegate invocation, an array indexing, a property access, a default property access, or an invocation of a parameterless function "e" followed by indexing. Which one of these it is, is determined by symbol resolution. After symbol resolution, as with C#, only method invocations are allowed as nameof arguments.

Result of the nameof operator. This depends on the form of the nameof argument…

nameof(simple-name), of the form I<A1…AK>
nameof(member-access), of the form E.I<A1…AK>
nameof(base-access-named), of the form base.I<A1…AK>

These cases are all resolved using the rules for simple name lookup $7.6.2 or member access $7.6.4. If they succeed in binding, they must bind to one of:
  • A method-group. This produces an error "To specify the name of a method, you must provide its arguments".
  • A variable, value, parameter, constant, enumeration-member, property-access, field, event, type-parameter, namespace or type. In this case the result of the nameof operator is simply "I", which is generally the name of the symbol that the argument bound to. There are some caveats…
If "I" identified an alias e.g. "using I = X.Y.Z; nameof(I)", then the result of nameof is still "I", even though the expression bound to the type X.Y.Z.

Also "I" undergoes the standard identifier transformations prior to being returned from nameof. In C# these are detailed in $2.4.2 of the C# spec: first any leading @ is removed, then Unicode escape sequences are transformed, and then any formatting-characters are removed. This of course still happens at compile-time. In VB, any surrounding [] is removed.

Also in VB the result has the capitalization that was used in the argument. This might be different from the capitalization of the symbol that the argument bound to.


nameof(invocation-expression), of the form F(args)

The invocation expression must be either a method-invocation because F was a method-group, or a delegate-invocation because F was a value of delegate type.
  • If a method-invocation, then F must have had the form E.I<A1…AK> or I<A1…AK> or base.I<A1…AK> from other rules in the language, and must have bound to a method-group. F(args) must bind to a unique method by the normal rules of overload resolution. The result of the nameof operator is simply I, subject to the same transformations as above.
  • If a delegate-invocation, it is a compile-time error "This expression does not have a name."
Coordinator
Oct 18, 2014 at 4:42 AM
Edited Oct 18, 2014 at 4:49 AM

Examples

void f(int x) {
   nameof(x)
}
// result "x": Parameter (simple name lookup)
int x=2; nameof(x)
// result "x": Local (simple name lookup)
const x=2; nameof(x)
// result "x": Constant (simple name lookup)
class C {
   int x;
   … nameof(x)
}
// result "x": Field (simple name lookup)
class C {
   void f() {}
   … nameof(f)
}
// result: error "To specify the name of a method, you must provide its arguments": Method-group (simple name lookup)
class C {
   void f() {}
   … nameof(f())
}
// result "f": Invocation-expression that resolves successfully
class C {
   void f(){}
   void f(int i){}
   … nameof(f(1))
}
// result "f": Invocation-expression that resolves successfully
Customer c; … nameof(c.Age)
// result "Age": Field (member access)
Customer c; … nameof(c._Age)
// result error "_Age is inaccessible due to its protection level: Private field (member access)
nameof(Tuple.Create)
// result error "To specify the name of a method, you must provide its arguments": Method-group (member access)
nameof(Tuple.Create(1,2))
// result "Create": Invocation-expression that resolves successfully, including with generic type inference
nameof(System.Exception)
// result "Exception": Type (member access)
nameof(List<int>)
// result "List": Type (simple name lookup)
nameof(List<>)
// result error "type expected": Unbound types are not valid expressions
nameof(List<int>.Length)
// result error "List<int> doesn't contain a member Length": Member access, is unable to find an static member named Length on this type
nameof(default(List<int>))
// result error "This expression doesn't have a name": Not one of the allowed forms of nameof
nameof(default(List<int>).Length)
// result "Length": Property access (member access)
nameof(int)
// result error "Invalid expression term 'int'": Not an expression. Note that 'int' is a keyword, not a name.
nameof(System.Int32)
// result "Int32": Type (member access)
using foo=System.Int32;
nameof(foo) 
// result "foo": Alias (simple name lookup)
nameof(System.Globalization)
// result "Globalization": Namespace (member access)
nameof(x[2])
nameof("hello")
nameof(1+2)
// error "This expression does not have a name": Not one of the allowed forms of nameof
NameOf(a!Foo)
' error "This expression does not have a name": VB-specific. Not one of the allowed forms of NameOf.
NameOf(dict("Foo"))
' error "This expression does not have a name": VB-specific. This is a default property access, which is not one of the allowed forms.
NameOf(dict.Item("Foo"))
' error "This expression does not have a name": VB-specific. This is an index of a property, which is not one of the allowed forms.
NameOf(arr(2))
' error "This expression does not have a name": VB-specific. This is an array element index, which is not one of the allowed forms.
Dim x = Nothing
NameOf(x.ToString(2))
' error "This expression does not have a name": VB-specific. This resolves to .ToString()(2), which is not one of the allowed forms.
Dim o = Nothing
Dim b As Func(Of Object, Object, Boolean) = AddressOf o.Equals
' result "Equals". Warning "Access of static member of instance; instance will not be evaluated": VB-specific. VB allows access to static members off instances, but emits a warning.
[Foo(nameof(C))]
class C {}
// result "C": Nameof works fine in attributes, using the normal name lookup rules.
[Foo(nameof(T))]
class C<T> {}
// result error "T is not defined": A class type parameter is not in scope in an attribute on that class
[Foo(nameof(T))] void f<T> { }
// result error "T not defined": A method type parameter is not in scope in an attribute on that method
void f([Attr(nameof(x))] int x) {}
// result error "x is not defined": A parameter is not in scope in an attribute on that parameter, or any parameter in the method
Function f()
  nameof(f)
End Function
' result "f": VB-specific. This is resolved as an expression which binds to the implicit function return variable
NameOf(New)
' result error "this expression does not have a name": VB-specific. Not one of the allowed forms of nameof. Note that New is not a name; it is a keyword used for construction.
Class C
  Dim x As Integer
  Dim s As String = NameOf(x)
End Class
' result "x": VB-specific. Field access (simple name lookup)
class C {
   int x;
   string s = nameof(x);
}
// result error "cannot reference non-static field": C#-specific. Normal  name referencing error
class C {
   int x;
   string s = nameof(C.x);
}
// error "C doesn't contain a member named x": Normal member access rules, with static/instance mismatch
class C {
   int x;
   string s = nameof(default(C).x);
}
// result "x": This is the C# idiom for getting the name of an instance variable.
struct S {
   int x;
   S() {var s = nameof(x); …}
}
// result "x": Field access (simple name lookup). Nameof argument is considered unreachable, and so this doesn't violate definite assignment.
int x; … nameof(x); x=1;
// result "x": Local access (simple name lookup). Nameof argument is unreachable, and so this doesn't violate definite assignment.
int x; nameof(f(ref x));
// result "f": Invocation expression. Nameof argument is unreachable and so can be used as a ref argument prior to definite assignment.
var @int=5; nameof(@int)
// result "int": C#-specific. Local (simple name lookup). The leading @ is removed.
nameof(m\u200c\u0065)
// result "me": C#-specific. The Unicode escapes are first resolved, and the formatting character \u200c is removed.
Dim [Sub]=5 : NameOf([int])
' result "int": VB-specific. Local (simple name lookup). The surrounding [.] is removed.
Coordinator
Oct 18, 2014 at 4:49 AM
Edited Oct 19, 2014 at 3:43 AM

Chief Open Design Question

Principle (2) says the nameof argument must resolve to one single symbol. Is this a principle worth sticking to? It could go either way. Let's examine the issues, starting with some concrete examples...
// Logging
void f(int i) {
    Log(nameof(f(i)), "method entry");
}
// Attributes
[DebuggerDisplay("={" + nameof(default(C).getString()) + "()}")]
class C {
    string getString() { ... }
}
If we decided to break principle (2), then we could allow method-groups, and hence omit the argument lists for these two examples. We could reasonably choose either to keep principle (2) or to break it, and there are pros and cons to both sides.

Comparison: code

void M(int i) {
}
void M(string s) { 
    var x = nameof(M);    // error with principle (2); okay without it
    var x = nameof(M(s)); // okay
    var x = nameof(M(s)); // okay
}

Comparison: logging code

// Logging
void f(int i) {
    Log(nameof(f(i)), "method entry"); // with principle (2) you have to supply arguments
    Log(nameof(f), "method entry"); // without it, you can omit arguments
}

Comparison: attributes

// Attributes
[DebuggerDisplay(nameof(default(C).getString())] // with principle (2) you supply parentheses
[DebuggerDisplay(nameof(default(C).getString)] // without it, you can omit them
class C {
    string getString() { ... }
}

Comparison: IDE behavior

With principle (2), IDE refactoring behavior will be understandable, e.g. if you rename M then you can anticipate how it will be renamed inside nameof arguments. It's easy to understand+predict what HighlightAllReferences and GoToDef and FindAllReferences will do.

Without principle (2), IDE refactoring becomes less predictable. What will happen if you rename-refactor the second "M" in Comparison:Code? Would it rename the first one as well, and also the nameof argument? It's also not clear how CodeLens and other tools would count nameof(M). Would it count it as a reference to all overloads of M? There are probably similar issues all over the IDE.

Comparison: typing burden

With principle (2), you always need to specify which overload of M you're referring to, even though they have the same name. (In mitigation, it's often quite easy to specify which overload. As in the logging case, you typically do it within the method you wish to name, and so you typically have parameters to hand).

Comparison: conceptual burden

Without principle (2), nameof will be the only part of the language where "method-group" is a valid expression. Everywhere else it must be resolved to a single method.

Comparison: analyzer burden

With principle (2), it will be easier to write analyzers. They won't blow up in untested corner cases involving nameof.
Oct 18, 2014 at 9:38 AM
Edited Oct 18, 2014 at 9:38 AM
This is getting awfully close to the infoof operator. Should we start holding our breath? :)

Did you consider allowing method groups only if it is not overloaded?
Pro: less typing. Most of the time methods don't have overloads.
Con: introducing an overload breaks the build.

Could you explain why you disallow indexers altogether? An indexed property does have an associated metadata entry with a name (usually Item or defined by IndexerNameAttribute), which sometimes occurs in PropertyChanged, e.g. WPF expects Item[] when the indexer property changes for some index value. It's not that big of a deal since we don't write custom collections implementing INotifyPropertyChanged all day long but I'd still like to know for the sake of completeness.
var x = nameof(M(i)); // okay
There's no i in scope. Shouldn't this be M(s)?
using I = X.Y.Z; nameof(I)
I think this should produce the same value as nameof(X.Y.Z) for consistency with typeof(I) and practically every other use case of an alias. An alias is just a compile-time artifact whereas pretty much all other expressions above have an associated IL metadata entry. We should be able to replace the alias with the concrete type without any semantic changes to the program.
Oct 18, 2014 at 11:12 AM
Edited Oct 18, 2014 at 2:25 PM
Hmm. I don't understand why we should call nameof(Func1(i)) instead of nameof(Func1). Even if there is else one Func1(double), for example, - name will be same Func1 cause it's not infoof design with exact func description. How about
public void Func1(int a){}
public void Func1(string s){}
var func1Name = nameof(Func1(i)); // ???
var func1Name1 = nameof(Func1(default(string)); //Seems resolved

public void Func2(int a, int b){}
public void Func2<T1>(int a, T1 b, bool b1 = false) where T1 : class
{}
var func2Name = nameof(Func2(i,j)); // ???
var func2Name2 = nameof(Func2(i,null)); //Ok, resolved, what about

public void Func3<T1>(int a, T1 b) where T1 : IDisposable
{}
var func3Name = nameof(Func3(default(int),default(IDisposable))); // Is it correct?

double i = 0.0;
var func1Name = nameof(Func1(i)); // ??? name conflict
cases?

**Added. Seems like default() resolving all issues, no need for removing (2) principle.
Oct 18, 2014 at 1:15 PM
agat50 wrote:
Hmm. I don't understand why we should call nameof(Func1(i)) instead of nameof(Func1). Even if there is else one Func1(double), for example, - name will be same Func1 cause it's not infoof design with exact func description.
They would both be "Func1", but if you decided to refactor the name of one of those Func1 functions the IDE would have no idea whether or not you would also want to change the name used in the nameof() clause.

to lwischik:
Rather than require actual argument values have you considered also allowing type names? It would save on typing of having to use default() everywhere.
int x = 123;
var func3Name = nameof(Func3(x, IDisposable));
As already mentioned, overload resolution was the big thing holding back the concept of an infoof() clause, if those concerns must be solved now could there still be time to implement that feature?

And I'm a little confused as to some of the restrictions. Why wouldn't nameof(int) be just as valid as nameof(foo) in that they're both effectively aliases of System.Int32? And why would nameof(System.Int32) be considered member access? Is that just for the sake of parsing?
Oct 18, 2014 at 3:34 PM
Halo_Four wrote:
to lwischik:
Rather than require actual argument values have you considered also allowing type names? It would save on typing of having to use default() everywhere.
int x = 123;
var func3Name = nameof(Func3(x, IDisposable));
Probably to keep it simple. nameof accepts a strict subset of completely valid C# expressions, the parser is already in place to handle these. To allow type names in those expressions you would
  • introduce constructs that are conceptionally new
  • need to determine that such constructs do not introduce ambiguity
  • write the parser. We're messing with overload resolution here. That's one of the more complex parts in the compiler.
  • test, etc.
    That seems an awful lot of work compared to the gain. Even though nameof(Func(default(A), default(B), default(C)) feels odd I'd still be satisfied with this tradeoff.
Oct 18, 2014 at 4:01 PM
BachratyGergely wrote:
Halo_Four wrote:
to lwischik:
Rather than require actual argument values have you considered also allowing type names? It would save on typing of having to use default() everywhere.
int x = 123;
var func3Name = nameof(Func3(x, IDisposable));
Probably to keep it simple. nameof accepts a strict subset of completely valid C# expressions, the parser is already in place to handle these. To allow type names in those expressions you would
  • introduce constructs that are conceptionally new
  • need to determine that such constructs do not introduce ambiguity
  • write the parser. We're messing with overload resolution here. That's one of the more complex parts in the compiler.
  • test, etc.
    That seems an awful lot of work compared to the gain. Even though nameof(Func(default(A), default(B), default(C)) feels odd I'd still be satisfied with this tradeoff.
Yeah, I understand. At this point in the game nobody wants to be introducing more work to an already complex part of the compiler. And if this is just a stepping point into such operators then we can live with syntax oddity for the time being and maybe it can get some additional polish in C#vNext, especially if it leads to an additional set of operators like infoof() or memberof().

Also, I double checked the above and while int is effectively treated like an alias to System.Int32 it is a keyword and cannot be re-aliased like another alias could be, e.g.:
using foo = int; // compiler error, int is a keyword
using bar = System.Int32; // fine
using baz = bar; // also fine
Just another one of those parser oddities that make something like nameof() feel half-baked, but it's understandable at least for now.
Oct 18, 2014 at 4:08 PM
Edited Oct 18, 2014 at 4:18 PM
I was wring the following when I read Halo_Four's post:
I find the fact that you can use a method's parameters as part of the nameof body to be a little

In order to explicitly choose the right simbol, why not go:
// Logging
void f(int i) {
    Log(nameof(f(int)), "method entry");
}
This way, you won't get developers doing something like "".Substring(44) (unless that wouldn't be a valid nameof body) with values that don't make sense and that doesn't mean anything aside from choosing a type for the parameter. I do agree it would probably need an whole new handling of "method calls" instead of re-using the language's syntax.
I therefor ask the same question as he is :)
Oct 18, 2014 at 4:58 PM
To add a little more context to the conversation:

Eric Lippert's Blog: In Foof We Trust

A good rundown on how complicated using types for overload resolution might be.

Eric's example obviously demonstrates problematic ambiguity, but the compiler already handles that ambiguity now with normal overload resolution. That ambiguity is also a bizarre edge-case which I don't think represents a real world scenario. After all, the C# compiler won't let you call the generic versions of the overloaded method without reflection, to my knowledge, so why not also require the programmer to break out the reflection if they want a MemberInfo for one or the other generic methods in this particularly odd case?

CIL has obviously solved this problem since a similar syntax is supported by ldtoken, how does C# appreciably differ?

My limited understanding of the parser as it stands today is that for overload resolution that it parses the expression for each argument, then infers their type, then compares the types to the possible method overloads. Does it change appreciably if the type inference is done by the programmer?

All that aside I understand that this may be more a question of time than of anything else.
Coordinator
Oct 19, 2014 at 3:51 AM
Edited Oct 19, 2014 at 3:54 AM

Comparisons

There are three good options on the table…
OPTION 0
nameof-expression: nameof ( [type-or-namespace .] identifier )
Meaning of Option0: this identifier must resolve to one or more instance or static (or maybe also extension members?) on that type/namespace. Or, if it's an identifier on its own, it can also resolve to variables, parameters, constants, or to 1 or more members of the current or base classes. Accessibility is mostly respected except that "protected" has to be treated a bit differently.
OPTION 1
nameof-expression: nameof ( expression )
Meaning of Option1: this expression must bind to either a named symbol or a method-group (i.e. several methods)
OPTION 2
nameof-expression: nameof ( expression )
Meaning of Option2: this expression must bind to a single named symbol

Comparison: IDE HighlightSymbol, GoToDef, RenameRefactoring behavior

The only reason that nameof even exists is to improve IDE behavior, to make renaming less error-prone. Let's see how it works with the different options:
class C {
    void f(int i)  {
        nameof(f)    // option 0/1
        nameof(f(i)) // option 2
    }
    static void f(string s) { }
}

static class Extensions {
    static void f(this C c, double d) { }
}
HighlightSymbol: when you set your cursor on the argument to nameof, options 0/1 would highlight all three declarations of f (instance, static and extension). Option 2 would only highlight the first declaration of f.

GoToDef: when you do GoToDef on the argument to nameof, options 0/1 would presumably have to pop up the "find references" window for you to select which one you want to go to (or give an error). Option 2 would go straight to the first declaration.

Rename declaration: when you rename the first declaration of f, options 0/1 would presumably leave the argument to nameof unchanged since it doesn't know whether nameof was referring to the first declaration of f or one of the other ones. That kind of defeats the whole point of nameof (which was to make renaming less error prone). Or maybe they would remain everything that was in the user's source code -- all three declarations of "f", and all nameof references to them. Option 2 would rename just the argument to nameof according to the renamed method declaration.

Rename argument: when you rename the argument to f, options 0/1 would presumably pop up an error dialog telling you they're unable to rename it; either that or they'd rename all three declarations of f. Option 2 would rename only the first declaration.

Comparison: IDE inline rename cleverness?

The IDE "smart rename" functionality will be used by most analyzers to figure out the best way to qualify a given name. Here's an example. Start with this code:
class Agent<INetwork, IDisk> {
    int p;
    void f(int _p) {
        p = _p;
        nameof(p);
    }
}
When you rename the method parameter to just "p", how does the inline rename avoid name-clashes? Like this:
class Agent<INetwork, IDisk> {
    int p;
    void f(int p)     {
        this.p = p;
        var s1 = nameof(Agent<,>.p);  // option 0
        var s2 = nameof(this.p);      // option 1/2
    }
}
The question is, do you prefer Agent<,>.p or this.p ?

Comparison: CodeLens

How many times do you think each method is referenced?
// OPTION 0/1:
class C {
    [3 references] void f(int i)    { Log(nameof(f), i); }
    [3 references] void f(string s) { Log(nameof(f), s); }
    [3 references] void f(object o) { Log(nameof(f), o); }
}

// OPTION 2:
class C {
    [1 references] void f(int i)    { Log(nameof(f(i)), i); }
    [1 references] void f(string s) { Log(nameof(f(s)), s); }
    [1 references] void f(object o) { Log(nameof(f(o)), o); }
}
I think options 0/1 are quite odd. Is each method really referenced three times? I think each method is referenced only once.

Comparison: code for this.p

My team convention is to always write "this" explicitly. What do I write inside nameof?
class Agent<INetwork, IDisk> {
    int p;
    void f() {
        var y = this.p;
        nameof(Agent<,>.p);  // option 0
        nameof(this.p);  // options 1/2
    }
}

Comparison: code for instance members

var x = GetAgent();
Log("{0} = {1}", nameof(Agent<,>.Length), x.Length); // option 0
Log("{0} = {1}", nameof(x.Length), x.Length); // option 1/2
Similar to "this.p", options 1/2 are easier all around when naming members of instances that I have in hand.

Comparison: code for attributes on the same class

[DebuggerDisplay(nameof(getString))] // option 0
[DebuggerDisplay(nameof(default(Agent<Network, Disk>).getString))] // option 1
[DebuggerDisplay(nameof(default(Agent<Network, Disk>).getString()))] // option 2
class Agent<INetwork, IDisk> {
    string getString() { … }
}
Where option 0 shines is when you don't have an instance in hand.

Comparison: code for attributes on a different class

[DependsOn(nameof(Agent<,>.getString))] // option 0
[DependsOn(nameof(default(Agent<Network, Disk>).getString()))] // option 1
[DependsOn(nameof(default(Agent<Network, Disk>).getString))] // option 2
class Peer { }
Actually, all three options are impossible in this scenario unless the method is public. Assuming it is public, again open 0 is better when I don't have an instance in hand, because (1) I don't need to provide concrete type arguments, and (2) I don't need the "default" keyword.

Comparison: code for name of current function

void f(int i) {
    nameof(f) // options 0/1
    nameof(f(i)) // option 2
}
Actually, this scenario is pretty unimportant. There's a much nicer way to get the name of the current member: [CallerMemberName].

Comparison: code for linq members

var best = (from a in agents orderby a.Cost select new { a.Name, a.Cost }).First();
Log("Cost = {0}", best.Cost); // impossible to use nameof with option 0
Log("{0} = {1}", nameof(best.Cost), best.Cost); // option 1/2
This is a pretty niche scenario. Option 0 only works for members of nameable types.
Oct 19, 2014 at 9:46 AM
The nameof I was looking for does not use parameters for methods, that is, I prefer option 0/1.

HighlightSymbol and GoToDef are fine for me. Rename declaration used to have a dialog box where you could check which ones do you want remove you know... anyway, if it is all or nothing, I would prefer all. Rename argument: if I understand correctly, the issue is renaming the argument i in function declaration to match the function name f and introduce conflict at the context of nameof. In that case, I prefer error to renaming things I didn't want to rename.

All of IDE inline rename cleverness?, code for this.p and code for instance members makes me prefer option 1.

Comparison: CodeLens are all strange for me. I would not consider the use in nameof to count towards references actually.

On the other hand, code for attributes on the same class makes me prefer option 0. In other words, I want to avoid using type qualifier wherever possible. It is obviously inevitable in the case of code for attributes on a different class but still, it is preferred. Making an instance of something just for nameof is a horrible syntax, especially when the semantics are making null and using its members - that would actually reasonably count to 'reference'.
(btw. options 1/2 are swapped in that example I believe)

I do not understand why it would be impossible to use option 0 in code for linq members. If the result is "Cost", how does it matter that type does not have a name?
Oct 19, 2014 at 1:47 PM
The more I hear about this, the more strongly I feel you should do Option 2 now.

Option 2 should always work.

If you do Option 2, can you also later add Option 1 or 0?

Perhaps Option 1 or 0 should be done later. The impact of the ambiguity is quite significant and each of the affected scenarios deserves significant thought.

I do not yet like Option 0, but this approach allows time for an extensive conversation regarding Options 0 and 1.

I think if so much had not already been cancelled you might consider cancelling this because of the late date. Of course no one really wants that, but I do think the late date means the most conservative approach is the best approach. I am defining late as not having very many public cycles left for comment.
Oct 20, 2014 at 11:30 AM
Option 2 seems the most straightforward to me. It would also be a trivial upgrade path from poor man's nameof:
private static string NameOf<T>(Expression<Func<T>> expr)
{
   // Some System.Linq.Expressions magic to get the member name of the outermost expression
}
// before
string name = ExpressionUtil.NameOf(() => some.Expression(a,b));
// after
string name = nameof(some.Expression(a,b));
Oct 20, 2014 at 3:21 PM
Regarding methods - I don't understand why we cannot have both worlds - easy and safe. First, I think we should allow to go without parameters if there is only single method. In case of multiple overloads, user should specify one explicitly. But it should be possible in simplest possible way:
nameof(ToString(string, IFormatProvider));
and not
nameof(ToString(default(string), default(IFormatProvider)));
Simple type name should be enough. Why should we go with default(IFormatProvider) when simple IFormatProvider carries the same information? Why should we be forced to introduce variables when there are e.g. out or ref parameters involved in method call?

Another thing: I cannot have instance and static members of the same name. Name is, well, name. It is meta. It should not matter if we are in "instance" or "static" context. Such information is irrelevant for the name of the member. It is weird that nameof(C.X) works with static X property of type C but does not work with instance property X of type C. Resulting name is the same after all and does not care about existence of static keyword. nameof(default(C).X) is too much ceremony for me. nameof(C.X) should work always.
As you may guess, I am not fan of nameof(this.p) for resolving field name. I think nameof(p) should be enough. Current class should be default when no other is provided. And if p clashes with name of local or parameter, or if one prefers to be explicit, then nameof(ClassName.x) should do the trick.

It also does not feel right I can write typeof(int) but I cannot write nameof(int).

And the last note. I don't like the visibility restriction at all. nameof is introduced to handle magic strings. One of important use case is reflection. And we resort to reflection when we cannot access things directly - to access internal and private things. It is very unfortunate we cannot use nameof in such case.

Summing up - I don't see what are advantages of situation where "instance" world is married with meta world. But I can see many issues with such marriage.
Oct 20, 2014 at 9:51 PM
As a tool developer I should prefer OPTION 2: it is MUCH more easier to implement this case.

But the invocation inside the nameof operator never calculates - it looks realy strange and misleading. So in my opinion OPTION 0 is not so bad.

OPTION 1 is trying to avoid arguments in invocations. It make sense. But there is also one more large problem except arguments: the need to use 'default' expressions. Possible we can add to OPTION 1 the possibility to invoke instance method like static (from OPTION 0). And make it more strict in this case: the last expression should looks like 'expression.identifier' without '<type arguments>' or '(invocation arguments)'. Something like OPTION 0 but with possibility to use any expression as qualifier.

The rename and other tool features should work without doubts if there is one candidate. And should operate with a set of candidates similar to the late bound case (when arguments are dynamic). When you rename one of the candidates the tool should warn about 'nameof' usage.
Oct 20, 2014 at 9:58 PM
I'm for keeping "Principle (2) says the nameof argument must resolve to one single symbol". Yes, it may be more cumbersome to write, but undoubtedly analyzers and other tools will be written to help completion for these invocations. It's better to be unambiguous.

Of course, if it is possible to allow method groups for the special case where there is only one overload, that would certainly be a nice optimization. True, it means adding an overload could break the build, but this is pretty low cost as it's just at build time, not run time. Since nameof is purely a compile-time sugar you don't have the risk of a 3rd party shipping a library that could be accidentally broken by client code.
Coordinator
Oct 21, 2014 at 6:57 PM
Based on feedback, I've produced v4 of the "nameof" spec. I started a new discussion thread for it. Let's close this thread, and all move to the new one.
https://roslyn.codeplex.com/discussions/570364
Marked as answer by lwischik on 10/21/2014 at 11:57 AM