nameof spec v4

Topics: C# Language Design, VB Language Design
Coordinator
Oct 21, 2014 at 6:01 PM
Edited Oct 22, 2014 at 1:02 AM
This is v4 of the spec for the "nameof" operator. [v1, v2, v3).
  • v1/2 had the problem "Why can't I write nameof(this.p)? and why is it so hard to write analyzers?"
  • v3 had the problem "Why can't I write nameof(MyClass1.p)? and why is it so hard to name method-groups?"
The key decisions are:
  1. Allow to dot instance off of class? Can you write nameof(MyClass1.p) or are you required to write nameof(default(MyClass1).p)? Let's pick the former because the latter is too ugly.
  2. Allow to dot instance off of instance? Can you write nameof(this.p); nameof(x.p) or are you required to write nameof(Customer<,>.p)? Let's pick the former because the latter is too ugly.
  3. Allow to name multiple things? Can you write nameof(x.M) or are you required to explicitly chose which overload nameof(x.M(i,j))? Let's pick the former because the latter is too ugly, and it misleadingly looks like the invocation will be executed, and it misleadingly looks like you're taking the name of the return value of method M. And let's lay down what the IDE experience will be, and what SemanticModel.GetSymbolInfo() will return.
  4. Emit source name or metadata name? Does using foo=X.Y.Z; nameof(foo); nameof(List<int>); nameof(T) give you metadata names "Z; List`1; error", or source names "foo; List; T" ? Let's pick the former in all three cases, so that nameof(AnyType) == typeof(AnyType).Name where possible.
  5. Roslyn syntax tree representation? Does NameOfSyntax represent its argument with an ExpressionSyntax or with a TypeNameSyntax.Identifier? Let's pick the former, because this way buggy codefixes will tend to produce user code that has an error squiggle, rather than generating an exception.
  6. What to do about expressions that look like they'll be executed? People may find it misleading in nameof(foo(x+1).Length) that method foo won't in fact be invoked. Let's ignore this problem. There's no benefit for users to write that kind of code since shorter alternatives are easier and don't have the ambiguity.
  7. What about predefined types? Is it allowed to write nameof(int) or other predefined types? Let's say "no". It's confusing that nameof(int)="Int32" and nameof(dynamic)="Object". Moreover, disallowing these cases makes for a simpler spec and simpler implementation.
  8. Name multiple types overloaded on arity? Can you write nameof(C) to refer to both C<T> and C<T,U> at the same? No, they have different metadata names.
  9. Ambiguous usings? Ambiguity due to using will still be an error, since the metadata names of the targets will likely differ.
Please let us know what you think!

nameof operator: spec round 4

The nameof(.) operator has the form nameof(expression). The expression must have a name, and may refer to multiple symbols. If the argument is a member-access, then it can include static, instance and extension members.

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(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(C.Age), typeof(int), typeof(C));
// Logging
void f(int i) {
    Log(nameof(f), "method entry");
}
// Attributes
[DebuggerDisplay("={" + nameof(C.getString) + "()}")]
class C {
    string getString() { ... }
}

C# Syntax

expression: ... | nameof-expression

nameof-expression:
    nameof ( simple-name )       x
    nameof ( member-access )     e.x, e.x<int>, e?.x,
                                 int.TryParse, a::b.c
    nameof ( 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:
    invocation-expression        e(args)
    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* 

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).

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.

Argument binding. The nameof argument will refer to one or more symbols as follows. (Member lookup always returns either a collection of static+instance+extension methods, or a single prop/field/event/const/subtype).


nameof(simple-name), of the form I<A1...AK> or I

The normal rules of simple name lookup $7.6.2 are used with some differences...
  • The third bullet talks about member lookup of I in T with K type arguments. Extension methods are ignored. If this matches some members or a type, then no substeps need be taken; the argument refers to all those members.
nameof(member-access), of the form E.I<A1...AK>
nameof(base-access-named), of the form base.I<A1...AK>

The normal rules of member access $7.6.4 are used with some differences...
  • Again, the third and fourth bullets talk about a member lookup of I in T with K type arguments. This includes extension method lookup, if it produces 1 or more matches, on substeps need be taken; the argument refers to all those members.
Result of nameof. The result of nameof depends on the symbols that its argument bound to:
  • A local variable, parameter or constant with name "I": the result of nameof is that string "I" after standard identifier transformations (see below).
  • A namespace with name "I": the result of nameof is that string "I" after standard identifier transformations. (Namespaces don't have metadata names)* A type parameter: this results in an error "This argument does not have a name"
  • One or more members: if all members have the same metadata name then the result of nameof is that name; otherwise it is an error "This argument refers to multiple elements with different names". The metadata name of a member I or I<A1...AK> is simply "I" after standard identifier transformations have been applied.
  • A type: the result of nameof is the metadata name of the type. For a non-generic type I, its metadata name is "I" after standard identifier transformations have been applied. For a generic type I<A1...IK>, its metadata name is "I" after transformations, followed by a backtick, followed by the integer number of arguments. For instance the metadata name of Dictionary<string,int> is Dictionary`2
Standard identifier transformations in C# 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
Coordinator
Oct 21, 2014 at 6:01 PM
Edited Oct 22, 2014 at 1:36 PM

Implementation

class NameOfExpressionSyntax : ExpressionSyntax { readonly ExpressionSyntax argument; }
What is the return value from var r = semanticModel.GetSymbolInfo(argument)? In all cases, r.Candidates is the list of symbol. If there is only one symbol then it is in r.Symbol; otherwise r.Symbol is null and the reason is "ambiguity".

Analyzers and the IDE will just have to deal with this case.

IDE behavior

class C {
   [3 references] static void f(int i) {...nameof(f)...}
   [3 references] void f(string s) {...nameof(this.f)...}
   [3 references] void f(object o) {...nameof(C.f)...}
}
static class E {
   [2 references] public static void f(this C c, double d) {}
}
Highlight symbol from argument: When you set your cursor on an argument to nameof, it highlights all symbols that the argument bound to. In the above examples, the simple name "nameof(f)" binds to the three members inside C. The two member access "nameof(this.f)" and "nameof(C.f)" both bind to extension members as well.

Highlight symbol from declaration: When you set your cursor on any declaration of f, it highlights all nameof arguments that bind to that declaration. Setting your cursor on the extension declaration will highlight only "this.f" and "C.f". Setting your cursor on any member of C will highlight both all three nameof arguments.

Goto Definition: When you right-click on an argument to nameof in the above code and do GoToDef, it pops up a FindAllReferences dialog to let you chose which declaration. (If the nameof argument bound to only one symbol then it would go straight to that without the FAR dialog.)

Rename declaration: If you do a rename-refactor on one of the declarations of f in the above code, the rename will only rename this declaration (and will not rename any of the nameof arguments); the rename dialog will show informational text warning you about this. If you do a rename-refactor on the last remaining declaration of f, then the rename will also rename nameof arguments. Note that if you turn on the "Rename All Overloads" checkbox of rename-refactor, then it will end up renaming all arguments.

Rename argument: If you do a rename-refactor on one of the nameof arguments in the above code, the rename dialog will by default check the "Rename All Overloads" button.

Expand-reduce: The IDE is free to rename "nameof(p)" to "nameof(this.p)" if it needs to do so to remove ambiguity during a rename. This might make nameof now bind to more things than it used to...

Codelens: We've articulated the rules about what the argument of nameof binds to. The CodeLens reference counts above are a straightforward consequence of this.

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": Member (simple name lookup)
class C {
   void f() {}
   nameof(f)
}
// result "f": Member (simple name lookup)
class C {
   void f() {}
   nameof(f())
}
// result error "This expression does not have a name"
class C {
   void f(){}
   void f(int i){}
   nameof(f)
}
// result "f": Members (simple name lookup)
Customer c; ... nameof(c.Age)
// result "Age": Member (member access)
Customer c; ... nameof(c._Age)
// result error "_Age is inaccessible due to its protection level: member (member access)
nameof(Tuple.Create)
// result "Create": members (member access)
nameof(System.Tuple)
// result "Tuple": Type (member access). This binds to the non-generic Tuple class; not to all of the Tuple classes.
nameof(System.Exception)
// result "Exception": Type (member access)
nameof(List<int>)
// result "List`1": Type (simple name lookup)
nameof(List<>)
// result error "type expected": Unbound types are not valid expressions
nameof(List<int>.Length)
// result "Length": Member (Member access)
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": member (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 "Int32": Type (simple name lookup, via alias)
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
NameOf(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(D))]
class C { class D {} }
// result "D": Members of a class are in scope for attributes on that class
[Foo(nameof(f))]
class C { void f() {} }
// result "f": Members of a class are in scope for attributes on that class
[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": Member (simple name lookup)
class C {
   int x;
   string s = nameof(x);
}
// result "x". Member (simple name lookup)
class C {
   int x;
   string s = nameof(C.x);
}
// result "x". Member (member access)
class C {
   int x;
   string s = nameof(default(C).x);
}
// result "x": Member (member access)
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 error "this expression does not have a name".
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([Sub])
' result "Sub": VB-specific. Local (simple name lookup). The surrounding [.] is removed.
class C {
  class D {}
  class D<T> {}
  nameof(C.D)
}
// result "D": member access only finds the type with the matching arity.
class C<T> where T:Exception {
  ... nameof(C<string>)
}
// result error: the type 'string' doesn't satisfy the constraints
Coordinator
Oct 21, 2014 at 7:03 PM
The chief question for debate, in the minds of the VB/C# language design team, is metadata name vs source name. I've written the above spec in terms of metadata name, because most (all?) of the commentators in the previous thread preferred it that way. Please speak up if you have opinions on the matter!


@BachratyGergely: don't hold your breath for infoof! The ugliness of v3 of the nameof spec was what brought it close to infoof.

@BachratyGergely: no point allowing nameof(p[]), since you can write nameof(p) which will be more understandable (and gives a simpler spec)

@Everyone, yes as you said the syntax for picking overloads was ugly, so I've removed it...

@Kathleen and others, I think you suggest we go for the simpler option2 to avoid this feature being cut. But we'd rather design it right than design it cheaply at this stage.
Oct 21, 2014 at 7:40 PM
Edited Oct 21, 2014 at 7:42 PM
I think all the changes are good, except for the rule for types: nameof(T) == typeof(T).Name.

If that is the case, then the nameof operator really is useless for types as it adds no new functionality. Under what circumstances would someone ever want List'1 emitted? I think emitting the name as it appears in source for types is far more useful.
Oct 21, 2014 at 7:43 PM
Edited Oct 21, 2014 at 7:48 PM
lwischik wrote:
The chief question for debate, in the minds of the VB/C# language design team, is metadata name vs source name. I've written the above spec in terms of metadata name, because most (all?) of the commentators in the previous thread preferred it that way. Please speak up if you have opinions on the matter!
Most of this seems pretty good. However I really wish there was a better answer to the whole overloaded method issue. The fact that nameof() does a shot-gun style reference to all of the potential overloads just feels really dirty, even if the end result would be the same (up until the inevitable accidental missed refactor). Not to mention that it seems like the last real problem that would need to be solved before really being able to achieve infoof() which would appeal to me significantly more than nameof() does.

I'll state the (probably) unpopular opinion here that I'd rather see this slide to C# 7.0 along with a good conversation around infoof() than to have some ambiguous syntax make it into C# 6.0 which would be incompatible with infoof() if that is ever implemented.

As for bread-and-butter cases, I don't know about you but logging is useless to me without full names. I currently grab the full stack trace to obtain the stack frame of the caller which I manually format into something usable, e.g. Namespace.Class<int>.Foo<string>(int,DateTime). I also don't want to force all callers to have to manually embed their information. Having a caller info attribute that could resolve a MethodBase of the caller would be vastly more useful to me here. Or at least caller info attributes to obtain the type name.
Coordinator
Oct 21, 2014 at 7:47 PM
MgSam wrote:
I think all the changes are good, except for the rule for types: nameof(T) == typeof(T).Name. If that is the case, then the nameof operator really is useless for types as it adds no new functionality. Under what circumstances would someone ever want List'1 emitted? I think emitting the name as it appears in source for types is far more useful.
New functionality: it's a constant and can appear in attributes.
Oct 21, 2014 at 7:56 PM
lwischik wrote:
MgSam wrote:
I think all the changes are good, except for the rule for types: nameof(T) == typeof(T).Name. If that is the case, then the nameof operator really is useless for types as it adds no new functionality. Under what circumstances would someone ever want List'1 emitted? I think emitting the name as it appears in source for types is far more useful.
New functionality: it's a constant and can appear in attributes.
Point taken.

Still, it seems bizarre that a language feature by design would emit what is essentially garble for generic types. It probably weakens the feature significantly, because once devs see that it outputs something useless for generic types, they might lose faith in it as a reliable tool.
Coordinator
Oct 21, 2014 at 7:59 PM
MgSam wrote:
Still, it seems bizarre that a language feature by design would emit what is essentially garble for generic types. It probably weakens the feature significantly, because once devs see that it outputs something useless for generic types, they might lose faith in it as a reliable tool.
I tried to approach it from the angle "What use is there to get the name of a programmatic element?"

My impression is that the dominant use is reflection (e.g. XAML, MVC, attributes for DebuggerDisplay, ...). Reflection will always be better with the metadata name.

Another use is for human-readable text e.g. in debug logging. For this, the metadata name is what people already see and it feels fine.


What uses do you see where the source-name is better?
Oct 21, 2014 at 8:11 PM
lwischik wrote:
Another use is for human-readable text e.g. in debug logging. For this, the metadata name is what people already see and it feels fine.

What uses do you see where the source-name is better?
I'm going to have to disagree with you on that point. I absolutely want to know the generic type parameters when logging. I do understand the conundrum you have when trying to have a single operator that covers so many use cases, especially with reflection which remains downright hostile towards generic types.
Developer
Oct 21, 2014 at 8:50 PM
If nameof(T) means typeof(T).Name when T binds to a type, then it can't be a compile-time constant for a type parameter.
Oct 21, 2014 at 9:16 PM
I believe you have a typo in the following point.

lwischik wrote:
  1. Emit source name or metadata name? Does nameof(List<int>) give you "List'1" or just "List"? Likewise does using foo=X.Y.Z; nameof(foo) give you "foo" or "Z"? Likewise does nameof(T) give you an error or "T"? Let's pick the former in all three cases, so that nameof(T) == typeof(T).Name.
For nameof(foo), the metadata name would be the latter case ("Z").

nmgafter wrote:
If nameof(T) means typeof(T).Name when T binds to a type, then it can't be a compile-time constant for a type parameter.
I can't tell if you are asking a question or explaining something. To clarify, according to point 4 nameof(T) (where T is a generic type parameter) will result in a compile-time error due to this fact.
Oct 21, 2014 at 9:17 PM
nmgafter wrote:
If nameof(T) means typeof(T).Name when T binds to a type, then it can't be a compile-time constant for a type parameter.
If it is a requirement that the end result be a compile-time constant I can understand that it should behave that way, but I think that will be contrary to what a lot of people will expect.

Not to mention, the use-case of using nameof() in an attribute to specify a type name for reflection won't work anyway as reflection expects full type names, generally fully qualified. Granted, it's common for said attributes to also accept a Type.

I reiterate my opinion, this feels less-than-half-baked. If something needs to be shipped I'd rather see it limited to locals, arguments, fields and properties where scope and ambiguity is not a factor and the conversation be picked up again later to deal with methods and types.
Coordinator
Oct 22, 2014 at 12:44 AM
Edited Oct 22, 2014 at 12:58 AM
nmgafter wrote:
If nameof(T) means typeof(T).Name when T binds to a type, then it can't be a compile-time constant for a type parameter.
I only meant nameof(AnyType) == typeof(AnyType).Name, i.e. an equality not a definition. I guess the operator == is undefined between "compile-time-error and value"...

I put it down as a compile-error to answer Halo_Four's point. I think people won't know what to expect for "nameof(T)" when T is a type parameter. Some will expect that nameof(T) will be the same as typeof(T).Name. Others will expect it to return the string "T". By making it an error, we sidestep both expectations in an up-front-discoverable way
Coordinator
Oct 22, 2014 at 12:46 AM
Edited Oct 22, 2014 at 1:01 AM
Halo_Four wrote:
Not to mention, the use-case of using nameof() in an attribute to specify a type name for reflection won't work anyway as reflection expects full type names, generally fully qualified. Granted, it's common for said attributes to also accept a Type.
Exactly. [Info(typeof(C), nameof(C.f))] will be enough to refer to a method in a non-overload case.


Halo_Four wrote:
I'm going to have to disagree with you on that point. I absolutely want to know the generic type parameters when logging
Please clarify. There are separate axes as to what the result of the nameof operator should be:
  1. Would you rather see it with backtick "List`1" or without "List" ?
  2. Would you rather see it fully qualified "System.Collections.Generic.List..." or not "List..." ?
  3. Would you rather see the generic type arguments present "List<int>", placeheld "List<>", or absent "List" ?
My question about whether you'd like to see the source name or metadata name was about axis (1). It sounds like you gave an answer about axis (3).
Coordinator
Oct 22, 2014 at 1:02 AM
sharwell wrote:
I believe you have a typo in the following point.
Yes! Thanks. I fixed it.
Oct 22, 2014 at 1:48 AM
lwischik wrote:
I tried to approach it from the angle "What use is there to get the name of a programmatic element?"

My impression is that the dominant use is reflection (e.g. XAML, MVC, attributes for DebuggerDisplay, ...). Reflection will always be better with the metadata name.

Another use is for human-readable text e.g. in debug logging. For this, the metadata name is what people already see and it feels fine.


What uses do you see where the source-name is better?
Actually my thinking was for logging purposes as well. When looking at logs it's more natural to see List or List<T> than List'1. Also, if you're trying to reconstruct the qualified type name as a string you'd want the source name. However, on further consideration, I think consistency and ease of binding may trump my earlier concern. You could always look up the type via reflection using the name. I withdraw my concern.

I'll be very happy to have this hole in the language finally filled. I've always hated the need for strings or run-time reflection to get names.
Oct 22, 2014 at 2:31 AM
lwischik wrote:
Halo_Four wrote:
Not to mention, the use-case of using nameof() in an attribute to specify a type name for reflection won't work anyway as reflection expects full type names, generally fully qualified. Granted, it's common for said attributes to also accept a Type.
Exactly. [Info(typeof(C), nameof(C.f))] will be enough to refer to a method in a non-overload case.
Yes, but if there is an overload that takes a type name and a method name, the following definitely won't work:
[Info(nameof(NS.C), nameof(NS.C.f))]
Shame the CLR doesn't currently support a string encoding to refer to a fully qualified method otherwise you could achieve something like the following without mucking about with strings:
[Info(methodof(C.f()))]
public class C {
    public void f() { }
}
Granted, that would require solving the overload resolution problem.
Halo_Four wrote:
I'm going to have to disagree with you on that point. I absolutely want to know the generic type parameters when logging
Please clarify. There are separate axes as to what the result of the nameof operator should be:
  1. Would you rather see it with backtick "List`1" or without "List" ?
  2. Would you rather see it fully qualified "System.Collections.Generic.List..." or not "List..." ?
  3. Would you rather see the generic type arguments present "List<int>", placeheld "List<>", or absent "List" ?
My question about whether you'd like to see the source name or metadata name was about axis (1). It sounds like you gave an answer about axis (3).
This is true, if the question is only between List and List'1 I'd definitely prefer the latter. I just wouldn't want to stop there.

I guess the only scenario in which I would personally find nameof() useful is so that I don't have to use string literals to refer to locals when throwing exceptions when validating arguments. Most of my other scenarios, such as logging, would be better served through additional caller info attributes.
Oct 22, 2014 at 3:13 AM
Edited Oct 22, 2014 at 3:34 AM
I think nameof(List<int) has no sense at all. For variables and methods we couldn't get List<int>.Func1 name, but typeof(List<int>) works fine already for everythink we want to know about the class. Yes, probably compile time is faster but it's only advantage (actually easy solving by caching). At most cases nameof generic class will be either useless(List'1) or giant(List<FullClassNameWithNamespaces>). What is usecase for nameof(List<int>)? Things like logging already have enough info about class name. Attributes - now we already can provide System.Type parameters, so no news here too...

Votes for key decisions:

1) nameof(default(MyClass1).p), no new syntax is good.
2) nameof(this.p); nameof(x.p) is more common usecase imho
3) Allow to name multiple things? - No, cause of refactoring issues.
4) what about generic params, will it be supported, if source name will be choosed?
public void Func1<T1>(T1 obj)
{
Console.Writeline(nameof(T1)); // ???
}
I think we don't need such metadata, cause it's 1) not clear usecases 2) movement to c++ templates\etc metaprogramming, which i think is a big question. I vote for more practical usage resolving, not for just cool things.
6) I think if we'll be using default(int) instead of i,j,(int)0 - it'll be easy separation to read with the codestyle.
7) Agree, no allowed.
Oct 22, 2014 at 9:32 AM
I don't understand, why will be:
using foo=System.Int32;
nameof(Int32) 
// result "foo": Type (simple name lookup, via alias)
What will be result in next case:
using System;
using foo=System.Int32;
nameof(Int32); 
nameof(foo);
My expectation, is that result should always be Int32. As I understand, there is no any metadata with name foo. So, it looks very strange for me to see it as result of nameof.

My thoughts about nameof(T) (generic argument). Really, we have in reflection argument type with name T in that case. So, it should be also resolved just to T. On other side, we need it so rarely, that it may be safer to restict it - as somebody may expect to see typeof(T).Name.

Also, I'd personally prefer to restrict overload method resolution for nameof, or at least allow to point on arguments (with default(...) or just type identifier) as in previous version - or at least allow to point it explicitly. In that case I've even prefer to have Warning otherwise.

I'd also hope to see infoof - so I've even to prefer wait with nameof if it will open much richer operator.
Oct 22, 2014 at 11:29 AM
Edited Oct 22, 2014 at 11:33 AM
Sorry guys, my $0.02 will be a bit long

lwischik wrote:
*1. Allow to dot instance off of class? Can you write nameof(MyClass1.p) or are you required to write nameof(default(MyClass1).p)? Let's pick the former because the latter is too ugly.
If it stays unambiguous then by all means allow the former.
*2. Allow to dot instance off of instance? Can you write nameof(this.p); nameof(x.p) or are you required to write nameof(Customer<,>.p)? Let's pick the former because the latter is too ugly.
Allow the former. Needed for anonymous types.
*3. Allow to name multiple things? Can you write nameof(x.M) or are you required to explicitly chose which overload nameof(x.M(i,j))? Let's pick the former because the latter is too ugly, and it misleadingly looks like the invocation will be executed, and it misleadingly looks like you're taking the name of the return value of method M. And let's lay down what the IDE experience will be, and what SemanticModel.GetSymbolInfo() will return.
As long as I still have the option to explicitly choose one specific overload when I want to I don't mind.
*4. Emit source name or metadata name? Does using foo=X.Y.Z; nameof(foo); nameof(List<int>); nameof(T) give you metadata names "Z; List`1; error", or source names "foo; List; T" ? Let's pick the former in all three cases, so that nameof(AnyType) == typeof(AnyType).Name where possible.
Emit metadata name. This way it's language-agnostic: no issues when porting between C# and VB or consuming libraries written in different languages. Might be a good idea to let nameof(Type) return the qualified name (with or without assembly qualifiers) since there is no such thing as a namespace in metadata.
*5. Roslyn syntax tree representation? Does NameOfSyntax represent its argument with an ExpressionSyntax or with a TypeNameSyntax.Identifier? Let's pick the former, because this way buggy codefixes will tend to produce user code that has an error squiggle, rather than generating an exception.
No clue. I'm with the majority :)
*6. What to do about expressions that look like they'll be executed? People may find it misleading in nameof(foo(x+1).Length) that method foo won't in fact be invoked. Let's ignore this problem. There's no benefit for users to write that kind of code since shorter alternatives are easier and don't have the ambiguity.
Just like #3: if an invocation expression is the only way to choose an overload, allow it.
*7. What about predefined types? Is it allowed to write nameof(int) or other predefined types? Let's say "no". It's confusing that nameof(int)="Int32" and nameof(dynamic)="Object". Moreover, disallowing these cases makes for a simpler spec and simpler implementation.
Currently Int32 and int are interchangable pretty much everywhere. I prefer keeping it that way, so allow nameof(int)="Int32". No opinion about nameof(dynamic).
*8. Name multiple types overloaded on arity? Can you write nameof(C) to refer to both C<T> and C<T,U> at the same? No, they have different metadata names.
Don't allow.
*9. Ambiguous usings? Ambiguity due to using will still be an error, since the metadata names of the targets will likely differ.
Don't allow. If it's a must allow with a warning.
// MVC Action links
<%= Html.ActionLink("Sign up",
             @nameof(UserController),
             @nameof(UserController.SignUp))
%>
This can only work if the name of the action is the same as the method, which is not always the case.
Funny thing is it is possible to write a little helper t4 with Roslyn that generates some boilerplate from the project source so that I can write this:
<a href="<%= TypedUrl.UserController.SignUp("some", "arguments") %>">...
Which is also type-safe for the action method arguments, not just the controller class and the action method name :)
class C {
   void f() {}
   nameof(f())
}
// result error "This expression does not have a name"
I would allow this to return "f". It's a fallback from nameof(f) way when I do want to specify an overload.
nameof(List<>)
// result error "type expected": Unbound types are not valid expressions
I would allow this to return "List'1". We may not have concrete types that satisfy the generic constraints, e.g. in a class library I may have a MyClass<T> where T : IMyInterface, new()
nameof(int)
// result error "Invalid expression term 'int'": Not an expression. Note that 'int' is a keyword, not a name.
I would allow this. int should be interchangable with Int32.
using foo=System.Int32;
nameof(Int32) 
// result "foo": Type (simple name lookup, via alias)
Return "Int32". If we stick to returning metadata names then a source name should never be used in place of a metadata name.
nameof(x[2])
nameof("hello")
nameof(1+2)
// error "This expression does not have a name": Not one of the allowed forms of nameof
If x[2] is a default property access, this should be allowed and return Item (or whatever the generated property name is).
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.
See above.
int x; nameof(f(ref x));
// result error "this expression does not have a name".
See #3
@BachratyGergely: no point allowing nameof(p[]), since you can write nameof(p) which will be more understandable (and gives a simpler spec)
I was talking about default property access off an instance, not an array element access. As I understand this is not possible at all in the current spec.
@Everyone, yes as you said the syntax for picking overloads was ugly, so I've removed it...
Also see #3. Sorry, #3 is a key point for me :)
@Kathleen and others, I think you suggest we go for the simpler option2 to avoid this feature being cut. But we'd rather design it right than design it cheaply at this stage.
I need to correct myself if it wasn't clear the first time. I'm not aiming for the cheap design but for the simple design. Which should be 1. easy to learn from the coder's perspective 2. easy to comprehend from the code reviewer's perspective 3. easy to maintain from the compiler team's perspective. That means reusing existing concepts and implementation as much as possible without polluting them (no new stuff to learn and don't implement the same functionality twice). Hence a common expression is an obvious candidate to start from.


Halo_Four wrote:
I'll state the (probably) unpopular opinion here that I'd rather see this slide to C# 7.0 along with a good conversation around infoof() than to have some ambiguous syntax make it into C# 6.0 which would be incompatible with infoof() if that is ever implemented.
I'm with you. If this feature can only bring about World Peace if pushed to C# 7.0 then by all means do so.
Coordinator
Oct 22, 2014 at 1:46 PM
darkman666 wrote:
I don't understand, why will be: using foo=System.Int32; nameof(Int32) // result "foo"
Oops, that was a typo! I meant to write nameof(foo) // result Int32.
My thoughts about nameof(T) (generic argument). Really, we have in reflection argument type with name T in that case. So, it should be also resolved just to T. On other side, we need it so rarely, that it may be safer to restict it - as somebody may expect to see typeof(T).Name.
Just for completeness, the code for your first thought is typeof(List<>).GetGenericArguments()[0].Name which returns string "T".
Coordinator
Oct 22, 2014 at 2:17 PM
Halo_Four wrote:
I reiterate my opinion, this feels less-than-half-baked. If something needs to be shipped I'd rather see it limited to locals, arguments, fields and properties where scope and ambiguity is not a factor and the conversation be picked up again later to deal with methods and types.
I'm coming to really like the idea of disallowing nameof(.) on types...
  • It's not needed because typeof(.) already does everything that nameof(.) does, and much more
  • Although typeof(.) isn't a constant, it's still allowed in attributes
  • This way we avoid the vexing question of "metadata name vs source name" which has no single good answer.
  • This way we avoid the vexing question of "nameof(int)" which, while possible, leads to a harder spec and implementation
  • This way we avoid the vexing question of "typeof(T) where T is a type parameter) which has no single good answer
  • This way we avoid the vexing question of "using foo=X.Y.Z; nameof(foo)" which has no single good answer
  • This way we avoid the vexing question of "nameof(List<>) vs nameof(List<int>)" which is a pain. (As for List<>.Element[0].Length vs List<string>.Element[0].Length, the one without type arguments doesn't make sense).
  • This way we avoid having to debate whether nameof(type) should end up being similar to typeof(type).Name or not, since each operator has a clearly different role
  • This way we avoid the question of "nameof(C)" referring to both C<T> and C<T,U> at the same time.
By avoiding types, it removes half the complexity and dubious design. (The other half of the complexity comes as you say from methods. But unlike types, those do have value!)
Oct 22, 2014 at 2:32 PM
lwischik wrote:
I'm coming to really like the idea of disallowing nameof(.) on types...
  • It's not needed because typeof(.) already does everything that nameof(.) does, and much more
  • Although typeof(.) isn't a constant, it's still allowed in attributes
  • This way we avoid the vexing question of "metadata name vs source name" which has no single good answer.
  • This way we avoid the vexing question of "nameof(int)" which, while possible, leads to a harder spec and implementation
  • This way we avoid the vexing question of "typeof(T) where T is a type parameter) which has no single good answer
  • This way we avoid the vexing question of "using foo=X.Y.Z; nameof(foo)" which has no single good answer
  • This way we avoid the vexing question of "nameof(List<>) vs nameof(List<int>)" which is a pain. (As for List<>.Element[0].Length vs List<string>.Element[0].Length, the one without type arguments doesn't make sense).
  • This way we avoid having to debate whether nameof(type) should end up being similar to typeof(type).Name or not, since each operator has a clearly different role
  • This way we avoid the question of "nameof(C)" referring to both C<T> and C<T,U> at the same time.
By avoiding types, it removes half the complexity and dubious design. (The other half of the complexity comes as you say from methods. But unlike types, those do have value!)
I agree it's simpler on the implementation side to not support it for types, however I'd prefer nameof(T) == typeof(T).Name over not supporting it. If you disallow it you introduce inconsistency- the developer has to know to use typeof(T).Name when they're used to using nameof. The reason for the restriction won't at all be apparent to them and will probably look arbitrary.
Oct 22, 2014 at 2:40 PM
Edited Oct 22, 2014 at 2:45 PM
+1. Restrict nameof to fields\local vars\properties\methods covers enough.
Oct 22, 2014 at 3:02 PM
Edited Oct 22, 2014 at 3:03 PM
lwischik wrote:
Halo_Four wrote:
I reiterate my opinion, this feels less-than-half-baked. If something needs to be shipped I'd rather see it limited to locals, arguments, fields and properties where scope and ambiguity is not a factor and the conversation be picked up again later to deal with methods and types.
I'm coming to really like the idea of disallowing nameof(.) on types...
  • It's not needed because typeof(.) already does everything that nameof(.) does, and much more
  • Although typeof(.) isn't a constant, it's still allowed in attributes
  • This way we avoid the vexing question of "metadata name vs source name" which has no single good answer.
  • This way we avoid the vexing question of "nameof(int)" which, while possible, leads to a harder spec and implementation
  • This way we avoid the vexing question of "typeof(T) where T is a type parameter) which has no single good answer
  • This way we avoid the vexing question of "using foo=X.Y.Z; nameof(foo)" which has no single good answer
  • This way we avoid the vexing question of "nameof(List<>) vs nameof(List<int>)" which is a pain. (As for List<>.Element[0].Length vs List<string>.Element[0].Length, the one without type arguments doesn't make sense).
  • This way we avoid having to debate whether nameof(type) should end up being similar to typeof(type).Name or not, since each operator has a clearly different role
  • This way we avoid the question of "nameof(C)" referring to both C<T> and C<T,U> at the same time.
By avoiding types, it removes half the complexity and dubious design. (The other half of the complexity comes as you say from methods. But unlike types, those do have value!)
I agree methods have a lot of value. It just concerns me (and I'm sure you even moreso) about the potential of stamping a less-than-optimal syntax into the language forever.

This is entirely my opinion and I'm sure that it is full of holes but this is along the syntax/behavior I would like and expect from nameof():
public class Class {
    public void Foo() { }

    public string Bar(int x, int y) { ... }
    public static string Bar(int x, int y, int z) { ... }

    public int Baz(string x, DateTime y) { ... }
    public string Baz(int x, DateTime y) { ... }

    private const string name1 = nameof(Foo); // only one Foo, not ambiguous, "Foo"
    private const string name2 = nameof(Class.Foo()); // fully qualified, "Foo"
    private const string name3 = nameof(this.Bar); // resolves to the instance Bar, "Bar"
    private const string name4 = nameof(Class.Bar); // resolves to the static Bar, "Bar"
    private const string name5 = nameof(Baz); // compiler error, ambiguous method Baz
    private const string name6 = nameof(Baz(int,DateTime)); // explicit overload resolution, "Baz"
    private const string name7 = nameof(Baz(string,DateTime)); // explicit overload resolution, "Baz"
}

public class Class2 {
    private const string name1 = nameof(Class.Bar); // compiler error, ambiguous between static and instance methods
    private const string name2 = nameof(Class.Bar(int,int)); // resolves to instance Bar, "Bar"
    private const string name3 = nameof(Class.Bar(int,int,int)); // resolves to static Bar, "Bar"
    private const string name4 = nameof(Class.Baz(short,DateTime)); // compiler error, requires explicit types for overload resolution.
}
It would be nice to have a better way to explicitly specify instance v. static methods, aside from perhaps allowing the use of this within the same class. Or perhaps specify this as the first parameter. C# does prevent identical signatures in static and instance methods but the CLR doesn't appear to.
Oct 22, 2014 at 4:04 PM
lwischik wrote:
I'm coming to really like the idea of disallowing nameof(.) on types...
By avoiding types, it removes half the complexity and dubious design. (The other half of the complexity comes as you say from methods. But unlike types, those do have value!)
+1 from me too :)

Halo_Four wrote:
It would be nice to have a better way to explicitly specify instance v. static methods, aside from perhaps allowing the use of this within the same class. Or perhaps specify this as the first parameter. C# does prevent identical signatures in static and instance methods but the CLR doesn't appear to.
While at it CLR also allows methods to differ only by return type. Can a C# compilation consume such methods? E.g.
// external
public class A
{
   public static void B();
   public void B();
   public int C() { return 0; }
   public string C() { return "0"; }
}
// consuming code
public class D : A
{
   public void Stuff()
   {
      B();   // is this static, instance or error?
      var c = C();   // is this int, string or error?
   }
}
This of course seems like an unrealistic corner case. Should this be addressed at all?
Oct 22, 2014 at 4:19 PM
BachratyGergely wrote:
While at it CLR also allows methods to differ only by return type. Can a C# compilation consume such methods? E.g.
// external
public class A
{
   public static void B();
   public void B();
   public int C() { return 0; }
   public string C() { return "0"; }
}
// consuming code
public class D : A
{
   public void Stuff()
   {
      B();   // is this static, instance or error?
      var c = C();   // is this int, string or error?
   }
}
This of course seems like an unrealistic corner case. Should this be addressed at all?
I'd agree. Purely academic. Neither C# nor VB.NET can consume these methods as they are considered ambiguous. F# appears to be able to both define and call instance and static methods that have the same name and apparent signature (ignoring the this parameter of course).
Oct 22, 2014 at 5:45 PM
MgSam wrote:
I agree it's simpler on the implementation side to not support it for types, however I'd prefer nameof(T) == typeof(T).Name over not supporting it. If you disallow it you introduce inconsistency- the developer has to know to use typeof(T).Name when they're used to using nameof. The reason for the restriction won't at all be apparent to them and will probably look arbitrary.
Really we have inconstancy here, because we have operator for types, that returns Type object, so, if we want to get name name, we should write typeof(SomeType).Name and for other members, we will have only name returning operator nameof. If only infoof will be implemented - there will be no any inconsistency - user will resolve method name with infoof(SomeMember).Name. If this can be used inside attributes, same as for types - it mostly overlap nameof functionality - but we still cannot resolve name of local variables in that case.
It doesn't look a problem for me if nameof will be not implemented for types. At the end, if it can be added later.
Oct 22, 2014 at 6:48 PM
Edited Oct 22, 2014 at 11:19 PM
lwischik wrote:
I'm coming to really like the idea of disallowing nameof(.) on types...
-1 for this for a couple of simple reasons:
  • nameof is naturally defined for source expressions that have a name. A type undoubtedly has a name. There may be a disagreement regarding what that name is, but simply cutting a bullet out of the list of permitted arguments for the sake of sparing a discussion makes the resulting feature really inconsistent. Note that most of the customers don't follow the history of this discussion, so, when you release this feature, they are going to be extremely dumbfounded as to why nameof permits any possible nameable thing inside of it, but not a type. Most of these people never even intended to use it on a generic type.
  • Regarding the source name vs. metadata name argument: at the end of the day, someone simply has to make a decision towards one or the other. This is a mindset that the C#/VB team has already applied in several similar circumstances (declaration expressions in else if come to mind first, as an example).
  • My two cents in the actual argument. nameof is expected to be a constant, a compile-time only artifact. Imagine a library that was originally written in VB.NET, but then someone decided to port it to C# line-by-line. This alone can be done without changing any of the resulting assembly metadata, therefore none of the library clients have to be recompiled. However, if NameOf and nameof return source names, the resulting constant will change after the translation, requiring for clients to be recompiled — a rather unexpected consequence, I'd say.
Oct 22, 2014 at 8:33 PM
lwischik wrote:
I'm coming to really like the idea of disallowing nameof(.) on types...
Really, using nameof(Type) could be good for attributes - we can't write anything like [SomeAttribute(typeof(SomeType).Name)]. But it will be possible to write [SomeAttribute(nameof(SomeType))]. Also, it seem's that same will answer for generic parameters resolution. Let's see next example:
public class Generic<T>
{
    [Some(nameof(T)]
    public static void Method()
    {
    }
}
nameof(T) could be resolved only to compile time constant T, it can't be resolved to typeof(T).Name for attribute.
Another question, if nameof will be support Type parameters - will it be possible to reference them only from inside class, or also outside? Something like nameof(Generic<>.T) or nameof(Generic<object>.T)?
Oct 22, 2014 at 9:03 PM
darkman666 wrote:
lwischik wrote:
I'm coming to really like the idea of disallowing nameof(.) on types...
Really, using nameof(Type) could be good for attributes - we can't write anything like [SomeAttribute(typeof(SomeType).Name)]. But it will be possible to write [SomeAttribute(nameof(SomeType))]. Also, it seem's that same will answer for generic parameters resolution. Let's see next example:
public class Generic<T>
{
    [Some(nameof(T)]
    public static void Method()
    {
    }
}
nameof(T) could be resolved only to compile time constant T, it can't be resolved to typeof(T).Name for attribute.
Another question, if nameof will be support Type parameters - will it be possible to reference them only from inside class, or also outside? Something like nameof(Generic<>.T) or nameof(Generic<object>.T)?
Except that nameof() as described above cannot be used for attributes, at least not in most cases where an attribute accepts a type name. The constant value provided is not enough for reflection to resolve the type from the name. For nameof(MyEntity) you'd only get "MyEntity", not "MyNamespace.MyEntity, MyAssembly" which is generally the minimum of what is required by reflection, and generic type parameters are apparently entirely off of the table. The problem is that making nameof() specifically reflection-friendly would make it notably unfriendly in most other use-cases.

In those cases you're much better off using typeof() and an overload of the attribute that accepts a System.Type. What the compiler actually embeds in that case is the fully qualified name of the type including all generic type parameters. To be able to use nameof() you'd require a reference to the assembly anyway so you wouldn't gain anything.
Oct 22, 2014 at 9:21 PM
How about I toss this one out there for poops and giggles? How about permit very specific expressions with typeof() resolve to compile-time constants? It would only need to apply in those cases when attempting to use the typeof() expression with an attribute that accepts a string.
// List`1
[MyTypeAttribute(typeof(List<>).Name)]
[MyTypeAttribute(typeof(List<int>).Name)]

// System.Collections.Generic.List`1
[MyTypeAttribute(typeof(List<>).FullName)]

// System.Collections.Generic.List`1, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
[MyTypeAttribute(typeof(List<>).AssemblyQualifiedName)]

// System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
[MyTypeAttribute(typeof(List<int>).FullName)]

// System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
[MyTypeAttribute(typeof(List<int>).AssemblyQualifiedName)]
What wouldn't be permitted is the use of this expression where a generic parameter would be resolved at run time, such as on a method-targeting attribute of a generic type:
public class Foo<T> {
    [MyTypeAttribute(typeof(Foo<T>).AssemblyQualifiedName)] // compiler error
    public void Bar() { }
}
This wouldn't give you a pretty source name but at least the developer can decide between a succinct name for logging purposes or a full name for reflection purposes.
Oct 22, 2014 at 9:26 PM
Edited Oct 23, 2014 at 6:11 AM
Halo_Four wrote:
In those cases you're much better off using typeof() and an overload of the attribute that accepts a System.Type. What the compiler actually embeds in that case is the fully qualified name of the type including all generic type parameters. To be able to use nameof() you'd require a reference to the assembly anyway so you wouldn't gain anything.
Fully agree, that in most case, nobody will use nameof with classes. At the same time, I agree with @Skiminok and @MgSam, that it may be strange for developers, why tehy can't use it with types.
So, first decision - will nameof work with type or not.
If it will work with type (I'd prefer that it does for consistency, but will not sorrow otherwise - as I can't found any real-world problems that it solves), we have another question - how it should work with generic type arguments. Here is 3 possible solution for nameof(T):
  • deny work with generic argument type - not too bad solution.
  • resolve it to typeof(T).Name - will not work with attributes at all. It will be very strange, that some nameof constructs would work in attribute and some - doesn't.
  • resolve it to name of generic parameter, so just T - will add some benefits to potential use with attributes or instead not refactoring safe construct typeof(TypeIdentifier<>).GetGenericArguments()[0].Name (we can replace order of arguments) - but may be strange for some developers, that expect to see typeof(T).Name.
Oct 23, 2014 at 2:45 AM
Edited Oct 23, 2014 at 2:47 AM
nmgafter wrote:
If nameof(T) means typeof(T).Name when T binds to a type, then it can't be a compile-time constant for a type parameter.
I'm a bit confused. Does nameof() return compile-time constant or runtime metadata name at all?
If it returns runtime metadata, then nameof(T) == typeof(T).Name, then it will be useless for scenarios like logging if the assembly is obfuscated.
If it returns compile time constant, then it may be different from typeof(T).Name if the assembly is obfuscated or post-processed. So, in this case, it is unnecessary and incorrect to say nameof(T) == typeof(T).Name, and source code name makes more sense to me.
Oct 23, 2014 at 8:08 AM
qrli wrote:
nmgafter wrote:
If nameof(T) means typeof(T).Name when T binds to a type, then it can't be a compile-time constant for a type parameter.
I'm a bit confused. Does nameof() return compile-time constant or runtime metadata name at all?
If it returns runtime metadata, then nameof(T) == typeof(T).Name, then it will be useless for scenarios like logging if the assembly is obfuscated.
If it returns compile time constant, then it may be different from typeof(T).Name if the assembly is obfuscated or post-processed. So, in this case, it is unnecessary and incorrect to say nameof(T) == typeof(T).Name, and source code name makes more sense to me.
nameof(expr) returns a compile time constant.
Oct 23, 2014 at 9:41 AM
Generally, I find the changes go in good direction.

Regarding types - I would still like nameof working with types. Sure, typeof works with attributes, but not when I need type name do define const field value. But I will not miss it that much if absent. If they should stay, I would like nameof to work with open generic types. First there are cases when we don't have type to serve as type parameter. Second, since nameof(Type) should return metadata name, it is unambiguous - nameof(List<>) should return the same as nameof(List<string>). Similar for nameof(Dictionary<,>). Specifying type arguments just provide noise.
I would also like it working with int and other builtin types. I don't see how Int32 can be confusing in such case. nameof returns metadata name, not source code name. It works the same for type aliases. I understand int is keyword, not type alias. But it is purely technical difference. From usage point of view, it is the same. But again - this is not killer feature and can be ironed later.
Anyway, whatever you pick, you will never please all of us when it comes to handling types. Just go with metadata name. It will simplify at least some use cases and has answers to few other follow up questions. For the rest use cases we can go with typeof.

I can't find anything targeted at my concerns with visibility. Will I be able to write nameof(C.M) outside of class C, where M is private method of type C?

And just curious - will below code work?
using System.Console;
class C {
  string s = nameof(WriteLine);
}
Oct 23, 2014 at 11:28 AM
It would be also very nice to have a memberof(class.member) operator in C#.
Oct 23, 2014 at 12:32 PM
RBancel wrote:
It would be also very nice to have a memberof(class.member) operator in C#.
That's pretty much what we're referring to when we mention infoof(), an operator to obtain the reflection metadata that is resolved at compile time like typeof().
Oct 23, 2014 at 12:51 PM
Przemyslaw wrote:
Second, since nameof(Type) should return metadata name, it is unambiguous - nameof(List<>) should return the same as nameof(List<string>). Similar for nameof(Dictionary<,>). Specifying type arguments just provide noise.
The question is how you intend to use that name. If you intend to just use it for diagnostics purposes, alright, the type parameters aren't critical, although you're only getting part of the puzzle if they're absent. If you intend to use it for reflection then those type parameters are critical because if they are erased you are getting a different unbound type which cannot be instantiated or used in most ways. Unlike Java a List'1 is not a List'1[Object] is not a List'1[Int32] is not a List'1[String], the type parameters are a part of the metadata name and I don't think they should be so quickly dismissed.
Oct 23, 2014 at 3:06 PM
Edited Oct 23, 2014 at 3:07 PM
Halo_Four wrote:
Przemyslaw wrote:
Second, since nameof(Type) should return metadata name, it is unambiguous - nameof(List<>) should return the same as nameof(List<string>). Similar for nameof(Dictionary<,>). Specifying type arguments just provide noise.
The question is how you intend to use that name. If you intend to just use it for diagnostics purposes, alright, the type parameters aren't critical, although you're only getting part of the puzzle if they're absent. If you intend to use it for reflection then those type parameters are critical because if they are erased you are getting a different unbound type which cannot be instantiated or used in most ways. Unlike Java a List'1 is not a List'1[Object] is not a List'1[Int32] is not a List'1[String], the type parameters are a part of the metadata name and I don't think they should be so quickly dismissed.
You can always construct it by calling nameof on every type argument you need. In many use cases you cannot go with List'1[Int32] either - you need System.Collections.Generic.List'1[System.Int32]. It is all about where to draw the line between simple nameof and fully operable typeof. I advocate to go with the simplest thing possible.
Oct 23, 2014 at 3:29 PM
Przemyslaw wrote:
You can always construct it by calling nameof on every type argument you need. In many use cases you cannot go with List'1[Int32] either - you need System.Collections.Generic.List'1[System.Int32].
Except that since nameof() omits the namespace you still can't construct a generic type with the resultant strings. You would need an additional nameof() not only for each individual type parameter but also for each level of the namespace for both the generic type and all of the type parameters. As it stands nameof() is pointless for reflection and cannot be used with attributes that expect to be provided a type name with which the attribute will attempt to resolve a type.
It is all about where to draw the line between simple nameof and fully operable typeof. I advocate to go with the simplest thing possible.
I would generally agree but the big question there is about use cases. If people expect that nameof() can be used with attributes that accept a type name then they will be surprised at the resultant exceptions that will occur at run time when the attribute can't do anything with the name.

I agree that there is value in being able to obtain a compile-time constant for a type name. My concern is whether the type name is consistently useful in the different scenarios in which the programmer would want that name. A method for the programmer to declare their intent further would be more useful. I don't imagine that what I propose will get consideration by the design team but I do think it covers most of those bases and I would extend upon it the ability to assign the value to constants.
public const string SimpleTypeName = typeof(List<int>).Name;  // List`1
public const string QualifiedTypeName = typeof(List<int>).AssemblyQualifiedName; // System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Coordinator
Oct 24, 2014 at 5:29 AM
I've started a new thread with v5 of the nameof spec. Let's move conversation over to there please!
https://roslyn.codeplex.com/discussions/570551
Marked as answer by lwischik on 10/23/2014 at 10:29 PM
Oct 24, 2014 at 4:39 PM
Halo_Four wrote:
I agree that there is value in being able to obtain a compile-time constant for a type name. My concern is whether the type name is consistently useful in the different scenarios in which the programmer would want that name. A method for the programmer to declare their intent further would be more useful. I don't imagine that what I propose will get consideration by the design team but I do think it covers most of those bases and I would extend upon it the ability to assign the value to constants.
Rather than try to define a single meaning for nameof() which will be useful in all cases, how about having an optional second parameter which would specify what is actually meant? So given George george;, the expression nameof(george.foo, short) would yield foo, nameof(george.foo, long) would yield george.foo; if the extra parameter were short typeof, yield George.foo; with long typeof yield GeorgeNamespace.George.foo, etc.

One could specify that certain formats will always yield constant strings, while others will yield constant strings when possible but might not always do so. Including const in the format specifier could request that compilation should fail rather than return a string that would need to be evaluated at runtime, for contexts where a compiler constant or non-constant string would be accepted, but a non-constant string would be much more expensive.

Incidentally, if a lambda in a generic method would need to close over the generic type parameter and nothing else, what would the compiler be expected to do with it? Would the compiler define a static generic class with all applicable type parameters and generate a static delegate the first time it was encountered and then use that same delegate for any future invocations? Given:
Func<String> GetFancyName<T>()
{
  return () => "The name of the type is " + typeof(T).getName().ToString();
}
what what would be the effect of repeated execution? Would it have to generate some sort of closure and delegate every time, or just once for each type T? My guess would be that the delegates would be cached but the string would have to be regenerated every time. Does that seem likely?