nameof operator - v5

Topics: C# Language Design, VB Language Design
Coordinator
Oct 24, 2014 at 5:18 AM
Edited Oct 29, 2014 at 6:53 PM

nameof operator: spec v5

The nameof(.) operator has the form nameof(expression). The expression must have a name, and may refer to either a single symbol or a method-group or a property-group. Depending on what the argument refers to, it can include static, instance and extension members.

This is v5 of the spec for the "nameof" operator. [v1, v2, v3, v4]. The key decisions and rationales are summarized below. Please let us know what you think!

Rationale

Question: why do we keep going back-and-forth on this feature?

Answer: I think we're converging on a design. It's how you do language design! (1) make a proposal, (2) spec it out to flush out corner cases and make sure you've understood all implications, (3) implement the spec to flush out more corner cases, (4) try it in practice, (5) if what you hear or learn at any stage raises concerns, goto 1.
  • 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?"
  • v4 had the problem "What name should be returned for types? and how exactly does it relate to member lookup?"
This particular "nameof v5" proposal came from a combined VB + C# LDM on 2014.10.22. We went through the key decisions:
  1. Allow to dot an instance member off a type? Yes. Settled on the answer "yes", based on the evidence that v1/v2 had it and it worked nicely, and v3 lacked it and ended up with unacceptably ugly "default(T)" constructions.
  2. Allow to dot instance members off an instance? Yes. Settled on the answer "yes", based on the evidence that v1/v2 lacked it and it didn't work well enough when we used the CTP, primarily for the case "this.p"
  3. Allow to name method-groups? Yes. Settled on the answer "yes", based on the evidence that v1/v2 had it and it worked nicely, and v3 lacked it and ended up with unacceptably ugly constructions to select which method overload.
  4. Allow to unambiguously select a single overload? No. Settled on the answer "no" based on the evidence that v3 let you do this but it looked too confusing. I know people want it, and it would be a stepping stone to infoof, but at LDM we rejected these (good) reasons as not worth the pain. The pain is that the expressions look like they'll be executed, and it's unclear whether you're getting the nameof the method or the nameof the result of the invocation, and they're cumbersome to write.
  5. Allow to use nameof(other-nonexpression-types)? No. Settled on the answer "only nameof(expression)". I know people want other non-expression arguments, and v1/v2 had them, and it would be more elegant to just write nameof(List<>.Length) rather than having to specify a concrete type argument. But at LDM we rejected these (good) reasons as not worth the pain. The pain is that the language rules for member access in expressions are too different from those for member access in the argument to nameof(.), and indeed member access for StrongBox<>.Value.Length doesn't really exist. The effort to unify the two concepts of member access would be way disproportionate to the value of nameof. This principle, of sticking to existing language concepts, also explains why v5 uses the standard language notions of "member lookup", and hence you can't do nameof(x.y) to refer to both a method and a type of name "y" at the same time.
  6. Use source names or metadata names? Source. Settled on source names, based on the evidence... v1/v2/v3 were source names because that's how we started; then we heard feedback that people were interested in metadata names and v4 explored metadata names. But I think the experiment was a failure: it ended up looking like disallowing nameof on types was better than picking metadata names. At LDM, after heated debate, we settled on source names. For instance using foo=X.Y.Z; nameof(foo) will return "foo". Also string f<T>() => nameof(T) will return "T". There are pros and cons to this decision. In its favor, it keeps the rules of nameof very simple and predictable. In the case of nameof(member), it would be a hindrance in most (not all) bread-and-butter cases to give a fully qualified member name. Also it's convention that "System.Type.Name" refers to an unqualified name. Therefore also nameof(type) should be unqualified. If ever you want a fully-qualified type name you can use typeof(). If you want to use nameof on a type but also get generic type parameters or arguments then you can construct them yourself, e.g. nameof(List<int>) + "`2". Also the languages have no current notion of metadata name, and metadata name can change with obfuscation.
  7. Allow arbitrary expressions or just a subset? We want to try out the proposal "just a subset" because we're uneasy with full expressions. That's what v5 does. We haven't previously explored this avenue, and it deserves a try.
  8. Allow generic type arguments? Presumably 'yes' when naming a type since that's how expression binding already works. And presumably 'no' when naming a method-group since type arguments are used/inferred during overload resolution, and it would be confusing to also have to deal with that in nameof. [this item 8 was added after the initial v5 spec]
I should say, we're not looking for unanimous consensus -- neither amongst the language design team nor amongst the codeplex OSS community! We hear and respect that some people would like something closer to infoof, or would make different tradeoffs, or have different use-cases. On the language design team we're stewards of VB/C#: we have a responsibility to listen to and understand every opinion, and then use our own judgment to weigh up the tradeoffs and use-cases. I'm glad we get to do language design in the open like this. We've all been reading the comments on codeplex and elsewhere, our opinions have been swayed, we've learned about new scenarios and issues, and we'll end up with a better language design thanks to the transparency and openness. I'm actually posting these notes on codeplex as my staging ground for sharing them with the rest of the team, so the codeplex audience really does see everything "in the raw".
Coordinator
Oct 24, 2014 at 5:19 AM
Edited Oct 29, 2014 at 7:35 PM

C# Syntax

expression: ... | nameof-expression

name-of-expression:
    nameof ( expression )
In addition to the syntax indicated by the grammar, there are some additional syntactic constraints: (1) the argument expression can only be made up out of simple-name, member-access, base-access, or "this", and (2) cannot be simply "this" or "base" on its own. These constraints ensure that the argument looks like it has a name, and doesn't look like it will be evaluated or have side effects. I found it easier to write the constraints in prose than in the grammar.

[clarification update] Note that member-access has three forms, E.I<A1...AK> and predefined-type.I<A1...AK> and qualified-alias-member.I. All three forms are allowed, although the first case E must only be made out of allowed forms of expression.

If the argument to nameof at its top level has an unacceptable form of expression, then it gives the error "This expression does not have a name". If the argument contains an unacceptable form of expression deeper within itself, then it gives the error "This sub-expression cannot be used as an argument to nameof".

It is helpful to list some things not allowed as the nameof argument:
    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            +, -, !, ~, ++, --,
                                 *, &, (T)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]
    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). 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 here's a selection of some of the types that are not expressions:
    predefined-type              int, bool, float, object,
                                 dynamic, string
    nullable-type                Customer?
    array-type                   Customer[,]
    pointer-type                 Buffer*, void* 
    qualified-alias-member       A::B
    void                         void
    unbound-type-name            Dictionary<,>

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.

Name lookup. In the following sections we will be discussing member lookup. This is discussed in $7.4 of the C# spec, and is left unspecified in VB. To understand nameof it is useful to know that the existing member lookup rules in both languages either return a single type, or a single instance/static field, or a single instance/static event, or a property-group consisting of overloaded instance/static properties of the same name (VB), or a method-group consisting of overloaded instance/static/extension methods of the same name. Or, lookup might fail either because no symbol was found or because ambiguous conflicting symbols were found that didn't fall within the above list of possibilities.

Argument binding. The nameof argument refers to one or more symbols as follows.

nameof(simple-name), of the form I or I<A1...AK>
The normal rules of simple name lookup $7.6.2 are used but with one difference...
  • The third bullet talks about member lookup of I in T with K type arguments. Its third sub-bullet says "Otherwise, the result is the same as a member access of the form T.I or T.I<A1...AK>. In this case, it is a binding-time error for the simple-name to refer to an instance member." For the sake of nameof(simple-name), this case does not constitute a binding-time error.
nameof(member-access), of the form E.I or E.I<A1...AK>
The normal rules of expression binding are used to evaluate "E", with no changes. After E has been evaluated, then E.I<A1...AK> is evaluated as per the normal rules of member access $7.6.4 but with some differences...
  • The third bullet talks about member lookup of I in E. Its sub-bullets have rules for binding when I refers to static properties, fields and events. For the sake of nameof(member-access), each sub-bullet applies to instance properties, fields and events as well.
  • The fourth bullet talks about member lookup of I in T. Its sub-bullets have rules for binding when I refers to instance properties, fields and events. For the sake of nameof(member-access), each sub-bullet applies to static properties, fields and events as well.
nameof(base-access-named), of the form base.I or base.I<A1...AK>
This is treated as nameof(B.I) or nameof(B.I<A1...AK> where B is the base class of the class or struct in which the construct occurs.


Result of nameof. The result of nameof is the identifier "I" with the standard identifier transformations. Note that, at the top level, every possible argument of nameof has "I<A1...AK>".

[update that was added after the initial v5 spec] If "I" binds to a method-group and the argument of nameof has generic type arguments at the top level, then it produces an error "Do not use generic type arguments to specify the name of methods". Likewise for property-groups.

The 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 24, 2014 at 5:19 AM
Edited Oct 29, 2014 at 7:00 PM

Implementation

In C#, nameof is stored in a normal InvocationExpressionSyntax node with a single argument. That is because in C# 'nameof' is a contextual keyword, which will only become the "nameof" operator if it doesn't already bind to a programmatic symbol named "nameof". TO BE DECIDED: what does its "Symbol" bind to?

In VB, NameOf is a reserved keyword. It therefore has its own node:
Class NameOfExpressionSyntax : Inherits ExpressionSyntax
    Public ReadOnly Property Argument As ExpressionSyntax
End Class
TO BE DECIDED: Maybe VB should just be the same as C#. Or maybe C# should do the same as VB.

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.

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",
             @typeof(UserController),
             @nameof(UserController.SignUp))
%>
// INotifyPropertyChanged
int p {
    get { return this._p; }
    set { this._p = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.p)); }
}
// also allowed: just nameof(p)
// XAML dependency property
public static DependencyProperty AgeProperty = DependencyProperty.Register(nameof(Age), typeof(int), typeof(C));
// Logging
void f(int i) {
    Log(nameof(f), "method entry");
}
// Attributes
[DebuggerDisplay("={" + nameof(getString) + "()}")]
class C {
    string getString() { ... }
}
Coordinator
Oct 24, 2014 at 5:19 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": 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": Method-group (simple name lookup)
Customer c; ... nameof(c.Age)
// result "Age": Property (member access)
Customer c; ... nameof(c._Age)
// result error "_Age is inaccessible due to its protection level: member access
nameof(Tuple.Create)
// result "Create": method-group (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": 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 error "This expression cannot be used for nameof": default isn't one of the allowed forms
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": Type (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
NameOf(o.Equals)
' result "Equals". Method-group. 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": Field (simple name lookup)
class C {
   int x;
   string s = nameof(x);
}
// result "x". Field (simple name lookup)
class C {
   static int x;
   string s = nameof(x);
}
// result "x". Field (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 error "This expression isn't allowed in a nameof argument" - default.
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" and binds to the non-generic form: 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 24, 2014 at 5:50 AM
@Przemyslaw: "Anyway, whatever you pick, you will never please all of us when it comes to handling types. Just go with metadata name."
I agree we'll never please everyone :)

@Przemyslaw: "Will this code work?" using System.Console; class C { nameof(WriteLine) }.
That's a good question. Yes it will work, because the "simple name lookup" rules have been modified to include static imports, and nameof re-uses those rules.

@Halo_Four: "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."
Just to be clear, with context, nameof is a great addition to typeof for reflection purposes, but not a good substitute for it. Most of the bread-and-butter cases use nameof to achieve stuff with reflection that couldn't be done well before - DependencyProperty.Register (which uses typeof() to get the type and then nameof() to get a related property); PropertyChangedEventArgs (where the type is implicit and nameof() gets the property); [DebuggerDisplay] (where the type is implicit and nameof() gets the method); inside a proposed HtmlAction (again using typeof() to get the type and then nameof() to get the method). It's not a universal solution of course - doesn't work with method or property overloads - but it's got good points.

@Halo_Four: "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".
Sure it will! You proposed that typeof(type).Name and typeof(type).AssemblyQualifiedName should be constants. What scenarios do you have in mind that need a constant, other than attributes which are already served by typeof? What would happen for the case of a generic type parameter where it can't be constant? What about the fact that compile-time constants would be based on reference assemblies which typically have different names from the runtime assemblies and so would have different AssemblyQualifiedNames? Do you see a clean way to alter the rules of member access to explain why these are constants? What would happen in ProjectN where assembly qualified names might not mean as much? Actually, most importantly, can you give code samples which would be cleaner under your proposal? (either as compared to VS2013, or compared to the nameof proposal?)
Oct 24, 2014 at 7:57 AM
lwischik wrote:
@Przemyslaw: "Anyway, whatever you pick, you will never please all of us when it comes to handling types. Just go with metadata name."
I agree we'll never please everyone :)

Source code name is as good as other choices. I am happy :)
One note more. Note, I am not VB'er, so it might be stupid question. What if in VB one will use
Public Shared AgeProperty As DependencyProperty = DependencyProperty.Register(NameOf(age), GetType(Integer), GetType(C))
Note usage of age to denote name of Age property. Will nameof(age) return "age" - taken from source in usage location - or "Age" - taken from source in declaration location?
Oct 24, 2014 at 8:23 AM
lwischik wrote:
  1. Allow to use nameof(other-nonexpression-types)? No. Settled on the answer "only nameof(expression)". I know people want other non-expression arguments, and v1/v2 had them, and it would be more elegant to just write nameof(List<>.Length) rather than having to specify a concrete type argument. But at LDM we rejected these (good) reasons as not worth the pain. The pain is that the language rules for member access in expressions are too different from those for member access in the argument to nameof(.), and indeed member access for StrongBox<>.Value.Length doesn't really exist. The effort to unify the two concepts of member access would be way disproportionate to the value of nameof. This principle, of sticking to existing language concepts, also explains why v5 uses the standard language notions of "member lookup", and hence you can't do nameof(x.y) to refer to both a method and a type of name "y" at the same time.
Totally agree with what's said about nameof.

However, I'd like some improvements on what an expression is that would allow List<>.Length to be an expression and, therefor, nameof(List<>.Length) to be valid.

lwischik wrote:
  1. Use source names or metadata names? Source. Settled on source names, based on the evidence... v1/v2/v3 were source names because that's how we started; then we heard feedback that people were interested in metadata names and v4 explored metadata names. But I think the experiment was a failure: it ended up looking like disallowing nameof on types was better than picking metadata names. At LDM, after heated debate, we settled on source names. For instance using foo=X.Y.Z; nameof(foo) will return "foo". Also string f<T>() => nameof(T) will return "T". There are pros and cons to this decision. In its favor, it keeps the rules of nameof very simple and predictable. In the case of nameof(member), it would be a hindrance in most (not all) bread-and-butter cases to give a fully qualified member name. Also it's convention that "System.Type.Name" refers to an unqualified name. Therefore also nameof(type) should be unqualified. If ever you want a fully-qualified type name you can use typeof(). If you want to use nameof on a type but also get generic type parameters or arguments then you can construct them yourself, e.g. nameof(List<int>) + "`2". Also the languages have no current notion of metadata name, and metadata name can change with obfuscation.
Isn't obfuscation a post compile-time task? Are you saying it can be a compile-time task?

Regarding string f<T>() => nameof(T) returning T, it makes sense as it's the only way to get a constant out of the expression, but i don't like that using foo=X.Y.Z; nameof(foo) returns foo. foo is an alias and T isn't.
Oct 24, 2014 at 12:18 PM
Looks good. I think the potential pain points can be discussed in a future rev if they are real pain points. For some of this, I wonder if we are actually looking at features better solved with compiler extensions – the ability to define things resolved at compile time.

First responses – not deeply thought
  • On NameOfExpressionSyntax in VB – (I am working with the API’s) don’t care. There are enough differences between the two that
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.
  • Do you have a mockup of the “rename dialog”. I hit Ctl-Period Enter without a dialog when I do rename refactor.
From the conversation

You proposed that typeof(type).Name and typeof(type).AssemblyQualifiedName should be constants.
  • Cool! Is that happening?
Oct 24, 2014 at 12:21 PM
Edited Oct 24, 2014 at 12:22 PM
I think allowing method groups and disallowing nameof(Func()) is mistake. As it was discussed earlier, what about refactoring? Why not allow both versions? And nameof(F1), and nameof(f1(default(int)) or nameof(f1(int)) whatsoever. Due to such behavior i'd rather use old slow expression handmade nameof for methods unfortunately.
Coordinator
Oct 24, 2014 at 1:53 PM
Przemyslaw wrote:
What if in VB you declare a property Age but then do NameOf(age) with different capitalization - will it pick argument or declaration capitalization?
Good question. There are other related cases. What if there was declared two overloads Sub fred() and Sub Fred() and you do NameOf(Me.FRED) which will it pick? Answer in all cases is that the argument to NameOf has the form "I" or "E.I" and the result is simply "I" -- i.e. use the capitalization of the argument to NameOf, not that of the declaration.


@PauloMorgado: the (small) point about obfuscation is that if there's a post-compile-time step that renames types, and you tried to make nameof return metadata names, then you might end up with nameof(Customer<int>) returning "Customer1" but typeof(Customer<int>).Name returning "_CS1" at runtime: it means that the compiler's attempt to anticipate the final metadata name was flawed.


@Kathleen: hit Ctrl+R Ctrl+R to get the inline rename experience. (At least that's the shortcut in General profile). I think there's an F key for it as well but I don't know. Good point that we'll also have to consider Ctrl+Dot.
Oct 24, 2014 at 2:36 PM
Edited Oct 24, 2014 at 2:41 PM
lwischik wrote:
@Halo_Four: "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."
Just to be clear, with context, nameof is a great addition to typeof for reflection purposes, but not a good substitute for it. Most of the bread-and-butter cases use nameof to achieve stuff with reflection that couldn't be done well before - DependencyProperty.Register (which uses typeof() to get the type and then nameof() to get a related property); PropertyChangedEventArgs (where the type is implicit and nameof() gets the property); [DebuggerDisplay] (where the type is implicit and nameof() gets the method); inside a proposed HtmlAction (again using typeof() to get the type and then nameof() to get the method). It's not a universal solution of course - doesn't work with method or property overloads - but it's got good points.
I spoke a little too absolutist in my original statement. I agree that nameof() does have various uses and can aid in reflection scenarios especially when resolving member names, and I think that it's great for resolving the names of local parameters.

These changes are nice but I don't think that they really address the core problem which is the potential confusion over how it is supposed to be used with type names. Returning "List" for List<int> isn't that much of an improvement since "List" is still not terribly useful.

And the issues with method groups and refactoring remains. I'd honestly rather attempting to reference overloaded methods to be a compile-time error until the conversation regarding overload resolution can be had. In that case it remains 99% useful now and doesn't codify an ambiguous behavior into the language for all time.
@Halo_Four: "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".
Sure it will! You proposed that typeof(type).Name and typeof(type).AssemblyQualifiedName should be constants. What scenarios do you have in mind that need a constant, other than attributes which are already served by typeof?
Thanks for giving me the opportunity to expand upon the idea.

These expressions would result in constants only when applied to scenarios expecting a constant, such as assignment to a constant or passing to an attribute. I understand that having two behaviors could be considered confusing, then again typeof() already has two behaviors for exactly this purpose although the implementation is pretty well hidden.
What would happen for the case of a generic type parameter where it can't be constant?
I'd probably go with a compile-time error. If the programmer intended to include an open generic type they could explicitly specify typeof(List<>) which is already legal today.
What about the fact that compile-time constants would be based on reference assemblies which typically have different names from the runtime assemblies and so would have different AssemblyQualifiedNames?
When using typeof() with an attribute a constant string containing the fully qualified assembly name, including version and public token, is what is embedded in the attribute value blob. Being fully qualified is optional if the type is in the current assembly or in mscorlib although C# always emits the fully qualified type unless the type is in the current assembly, which is understandable as the compiler likely can't resolve that information at the time it would have to be emitted. As such using typeof(Foo).AssemblyQualifiedName on a local type in such a situation should probably be a compile-time error.
Do you see a clean way to alter the rules of member access to explain why these are constants?
That would probably require more text than would be reasonable to include in this forum. :) I'd probably have to look at how the spec currently handles the duality of typeof(), although granted that doesn't resolve as a member access. I can understand that this would be a sticking point because I am redefining a construct of the language.
What would happen in ProjectN where assembly qualified names might not mean as much? Actually, most importantly, can you give code samples which would be cleaner under your proposal? (either as compared to VS2013, or compared to the nameof proposal?)
It's more a question of intent. The problem I have with nameof() with types is that there are multiple scenarios in which a type name could be useful and the value of that type name would be different for those scenarios. Having typeof() support this kind of syntax (or through another syntax) gives the programmer the opportunity to be explicit. The prototypical code example would probably be as follows:
[AttributeUsage(AttributeTargets.Class)]
public class SomeAttribute : Attribute {
    public string TypeName { get; set; }
}

[SomeAttribute(typeof(Bar).AssemblyQualifiedName)]
public class Foo {
    private const string FallbackType = typeof(Baz).AssemblyQualifiedName;

    public Foo() {
        object[] attributes = typeof(Foo).GetCustomAttributes(typeof(SomeAttribute), false);
        SomeAttribute attribute = (SomeAttribute)attributes[0];
        Type type = Type.GetType(attribute.TypeName);
        if (type == null) {
            type = Type.GetType(FallbackType);
        }
        object instance = Activator.CreateInstance(type);
    }
}
Oct 24, 2014 at 3:36 PM
lwischik wrote:
Przemyslaw wrote:
What if in VB you declare a property Age but then do NameOf(age) with different capitalization - will it pick argument or declaration capitalization?
Good question. There are other related cases. What if there was declared two overloads Sub fred() and Sub Fred() and you do NameOf(Me.FRED) which will it pick? Answer in all cases is that the argument to NameOf has the form "I" or "E.I" and the result is simply "I" -- i.e. use the capitalization of the argument to NameOf, not that of the declaration.
Might be a good idea to have an IDE warning about casing. WPF bindings will not update if the argument to nameof is cased incorrectly in property change.
Oct 24, 2014 at 3:49 PM
lwischik wrote:
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.
Is the above behavior affected by the location of overloads in either the present class or superclasses? For example, if a class contains an overload for foo(int) and no others, but the superclass contains others overloads, would nameof(this.foo) be unaffected? What if the only overload in the base class was foo(int), which would become accessible when the child-type definition was renamed?

I would guess that logical behavior would be to base the decision of whether to rename the nameof() operands depend upon whether there was more than one definition of the name visible in the present scope before the rename, not whether there would be any definitions with that name visible in the present scope afterward, but it might be good to clarify that.
Oct 24, 2014 at 4:51 PM
Halo_Four wrote:
And the issues with method groups and refactoring remains. I'd honestly rather attempting to reference overloaded methods to be a compile-time error until the conversation regarding overload resolution can be had. In that case it remains 99% useful now and doesn't codify an ambiguous behavior into the language for all time.
Allowing method groups now doesn't preclude a syntax for choosing overloads at some future date. However, I think it's important to keep in mind during this discussion that after nameof is added, the benefit for then adding infoof later becomes even less worth the cost.

I'm fine with the new spec. The only thing I really don't like is that nameof on an alias gives you the name of the alias rather than the target of the alias. Even though implementation and spec might be simpler by doing this, it's still a misfeature- when would anyone ever want this behavior? It's bound to lead to unintentional bugs.
Oct 24, 2014 at 7:54 PM
MgSam wrote:
Halo_Four wrote:
And the issues with method groups and refactoring remains. I'd honestly rather attempting to reference overloaded methods to be a compile-time error until the conversation regarding overload resolution can be had. In that case it remains 99% useful now and doesn't codify an ambiguous behavior into the language for all time.
Allowing method groups now doesn't preclude a syntax for choosing overloads at some future date. However, I think it's important to keep in mind during this discussion that after nameof is added, the benefit for then adding infoof later becomes even less worth the cost.
I agree, but even if a new syntax is added to support overload resolution we'll be permanently left with the ambiguous version. Best we could hope for on revision is a warning.

What are the use cases for taking the name of a method group like that, anyway? Any reflection scenario I can think of would probably fail at run time due to the ambiguity of the name. Whatever consumed the name would have to manually resolve the desired override by method signature.
I'm fine with the new spec. The only thing I really don't like is that nameof on an alias gives you the name of the alias rather than the target of the alias. Even though implementation and spec might be simpler by doing this, it's still a misfeature- when would anyone ever want this behavior? It's bound to lead to unintentional bugs.
Hm, in the v4 spec lwischik mentioned that this was a typo and that the result was supposed to be "Int32". I wonder if he changed his mind or just made the same typo.
Oct 24, 2014 at 8:39 PM
Halo_Four wrote:
Hm, in the v4 spec lwischik mentioned that this was a typo and that the result was supposed to be "Int32". I wonder if he changed his mind or just made the same typo.
That's not a typo in this case, I think, In v4, nameof consistently returned metadata names, whereas in v5, it consistently returns source names. Since "foo" is, in fact, a source name for Int32 in this case, that is what nameof is going to return.

I don't like it at all, frankly speaking. There's a difference between "T" in string f<T>() => nameof(T) and "foo" in using foo=X.Y.Z; nameof(foo). The first one returns a source name for an object that exists both at compile-time and at run-time — a generic type argument. Even though at run-time this argument is replaced with a specific type, the notion of a generic type argument still exists in metadata.
In contrast, type aliases are purely a source-only feature. Reflection doesn't know anything about type aliases. I cannot imagine any possible usage for returning a constant string that denotes a compile-time only object... but at run-time.

I personally have always thought about type aliases in terms of α-conversion. A type alias "foo" is simply a local renaming for a type "Int32". It is an alias, an indirect reference, which disappears at run-time. Giving an evidence of its existence at run-time with nameof breaks that neat encapsulation philosophy.
Oct 25, 2014 at 2:11 AM
Skiminok wrote:
Halo_Four wrote:
Hm, in the v4 spec lwischik mentioned that this was a typo and that the result was supposed to be "Int32". I wonder if he changed his mind or just made the same typo.
That's not a typo in this case, I think, In v4, nameof consistently returned metadata names, whereas in v5, it consistently returns source names. Since "foo" is, in fact, a source name for Int32 in this case, that is what nameof is going to return.
You're right, it's point 6 of the v5 spec.
I don't like it at all, frankly speaking. There's a difference between "T" in string f<T>() => nameof(T) and "foo" in using foo=X.Y.Z; nameof(foo). The first one returns a source name for an object that exists both at compile-time and at run-time — a generic type argument. Even though at run-time this argument is replaced with a specific type, the notion of a generic type argument still exists in metadata.
In contrast, type aliases are purely a source-only feature. Reflection doesn't know anything about type aliases. I cannot imagine any possible usage for returning a constant string that denotes a compile-time only object... but at run-time.

I personally have always thought about type aliases in terms of α-conversion. A type alias "foo" is simply a local renaming for a type "Int32". It is an alias, an indirect reference, which disappears at run-time. Giving an evidence of its existence at run-time with nameof breaks that neat encapsulation philosophy.
And therein lies the crux of the problem. You can pick metadata names or you can pick source names and either way you can guarantee that you'll still be wrong half of the time. I'll argue that I think that they should be metadata names, but complete metadata names and not partial names.

I'll ask another unpopular question: do we really need to get the names for all of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft.

Oh, and I keep seeing logging bandied about as a use case for nameof(). That's ludicrous. Why should the programmer ever have to have the caller explicitly specify that it wants to be logged? Isn't that why caller info attributes exist? And I still think that they need to be expanded upon because the current batch is horribly limited.
Oct 25, 2014 at 3:00 AM
Edited Oct 25, 2014 at 3:26 AM
Halo_Four wrote:
I'll ask another unpopular question: do we really need to get the names for all of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft.
I totally agree, nameof use-cases are mostly notifypropertychanged, log, raise exception\etc, no need to do universal nameof due to release is coming soon=).

And i want say something about infoof() future operator. Do we actually need it? In c++ compile time types are crucial feature, "vector<string>::const_iterator" etc are used everywhere, but c# has absolutely another design. For example, as i see possible infoof usage:
public class A
{
    public void Func1(int a, double b)
    {}
    private infoof(A.Func1(int,double)).FirstParameter.ParameterType _f1
        = default(infoof(A.Func1(int,double)).FirstParameter.ParameterType);
    public infoof(A.Func1(int,double)).ReturnType Func2(
        string s, 
        infoof(A.Func1(int,double)).SecondParameter.ParameterType b
    )
    {
        Console.WriteLine(
            "{0},{1},{2}",
            s,
            b,
            infoof(A.Func1(int,double)).SecondParameter.ParameterName
        );
    }
}
I don't see ANY use-case for such complex feature which costs effort. So probably we should just stick to common use-cases nameof() syntax without making it Big feature, which is useless, imho.
Developer
Oct 25, 2014 at 3:42 PM
Halo_Four wrote:
I'll ask another unpopular question: do we really need to get the names for all of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft.
Failure of imagination - that is, failure to make language constructs orthogonal because you aren't quite sure how people will need to use them together - is one of the most common and severe errors you can make in programming language design. It is an error that is often impossible to correct after the fact. There is an approach to programming language design that you can take to maximize your opportunities to err in this way: consider the interactions of individual language features and decide on a case-by-case basis if the interaction will be allowed or not (or even what the semantics should be) based on whether or not you can find use cases and which semantics best fit those use cases.
Oct 25, 2014 at 5:49 PM
nmgafter wrote:
Halo_Four wrote:
I'll ask another unpopular question: do we really need to get the names for all of those things? Are they being implemented because there is a purpose in doing so, or because they're technically easy to resolve? I can't think of any scenario in which I'd want to get just the last portion of a dotted namespace name. And the name of an alias? Well, frankly, the options of the alias or the hobbled type name both don't make any sense to me. Locals, parameters, properties, methods, yeah, that all makes sense and there are use cases. The rest? I think it's cruft.
Failure of imagination - that is, failure to make language constructs orthogonal because you aren't quite sure how people will need to use them together - is one of the most common and severe errors you can make in programming language design. It is an error that is often impossible to correct after the fact. There is an approach to programming language design that you can take to maximize your opportunities to err in this way: consider the interactions of individual language features and decide on a case-by-case basis if the interaction will be allowed or not (or even what the semantics should be) based on whether or not you can find use cases and which semantics best fit those use cases.
I can understand that. However, nameof() is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax.

My concern is the opposite of your statement. Once the behavior of the language feature is stamped and released we're all stuck with it permanently. Why dig pits before you can anticipate if they'll be success or failure?

Don't get me wrong, I think the majority of scenarios with nameof() make perfect sense and I think that it is a worthwhile feature. In some other cases I agree that the scenario is conceptually useful but that there isn't a "right answer" as to what the return value should be and that nameof() by itself doesn't allow the programmer to declare enough of their intent. Even with namespaces, how do you know that the programmer doesn't want or need the full namespace? Does the programmer need to break out and concat multiple uses of nameof()? Are we walking into a fullnameof() in C# 7.0?
Oct 27, 2014 at 9:21 PM
Halo_Four wrote:
Does the programmer need to break out and concat multiple uses of nameof()? Are we walking into a fullnameof() in C# 7.0?

What about having an optional second argument to nameof() which would specify what is desired? That would seem better than trying to come up with one meaning to please everyone.
Developer
Oct 27, 2014 at 11:06 PM
Halo_Four wrote:
I can understand that. However, nameof() is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax.
nameof() is already orthogonal. We restrict the scenarios in which it can be used in C# 5 to no scenarios.

Orthogonal means that whether or not it is allowed, and what it means, is independent of the "scenario".

As described above, we avoid designing language features scenario by scenario.
Oct 27, 2014 at 11:52 PM
Edited Oct 28, 2014 at 12:13 AM
nmgafter wrote:
Halo_Four wrote:
I can understand that. However, nameof() is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax.
nameof() is already orthogonal. We restrict the scenarios in which it can be used in C# 5 to no scenarios.

Orthogonal means that whether or not it is allowed, and what it means, is independent of the "scenario".

As described above, we avoid designing language features scenario by scenario.
Which is absolutely great. But how do you design language features that have no "correct" answers without looking at the scenarios? How do you know how people expect that feature to behave? If you end up with a ton of people on Stackoverflow asking why nameof() explodes at runtime when used with attributes is that considered a success? I don't think that any feature can be designed in a bottle.

Because nameof() is so cross-cutting you are designing it scenario-by-scenario whether you want to or not because there are a ton of different scenarios in which it can be employed syntactically and you have to evaluate each of the results independently. If that wasn't the case you wouldn't have the two-dozen or so different ways to figure out the result and we wouldn't be on v5 where the real bread-and-butter use-cases haven't changed at all.

Update: Rereading my comment I realize that I am probably coming off as belligerent or even angry. I want everyone to know that is the furthest from the case. I really enjoy these conversations and find the back and forth really enlightening both about the design process and as to the different approaches one can take to tackle these problems. Given any proposal I would probably seek to poke holes in it if only as a devil's advocate. I have a great deal of passion about this language and am very grateful for the effort that has gone into making it what it is and will continue to make it great in the future. I applaud the decision that made this process a bit more public than it has been in the past and apologize for the grief that these forums impose of those who work on the front lines. :)
Oct 27, 2014 at 11:54 PM
supercat wrote:
Halo_Four wrote:
Does the programmer need to break out and concat multiple uses of nameof()? Are we walking into a fullnameof() in C# 7.0?

What about having an optional second argument to nameof() which would specify what is desired? That would seem better than trying to come up with one meaning to please everyone.
Either way, my comment was one of capability, not implementation. Whether it might be fullnameof(type) or nameof(type, full) or some constant-evaluating flavor of typeof(type).FullName it's all about whether or not the developer can declare their intent.
Oct 28, 2014 at 1:05 AM
Halo_Four wrote:
nmgafter wrote:
Halo_Four wrote:
I can understand that. However, nameof() is already orthogonal. Restricting the scenarios in which it can be used today doesn't at all prevent those scenarios from being added tomorrow since you wouldn't be redefining what would be currently legal syntax.
nameof() is already orthogonal. We restrict the scenarios in which it can be used in C# 5 to no scenarios.

Orthogonal means that whether or not it is allowed, and what it means, is independent of the "scenario".

As described above, we avoid designing language features scenario by scenario.
Which is absolutely great. But how do you design language features that have no "correct" answers without looking at the scenarios? How do you know how people expect that feature to behave? If you end up with a ton of people on Stackoverflow asking why nameof() explodes at runtime when used with attributes is that considered a success? I don't think that any feature can be designed in a bottle.

Because nameof() is so cross-cutting you are designing it scenario-by-scenario whether you want to or not because there are a ton of different scenarios in which it can be employed syntactically and you have to evaluate each of the results independently. If that wasn't the case you wouldn't have the two-dozen or so different ways to figure out the result and we wouldn't be on v5 where the real bread-and-butter use-cases haven't changed at all.

Update: Rereading my comment I realize that I am probably coming off as belligerent or even angry. I want everyone to know that is the furthest from the case. I really enjoy these conversations and find the back and forth really enlightening both about the design process and as to the different approaches one can take to tackle these problems. Given any proposal I would probably seek to poke holes in it if only as a devil's advocate. I have a great deal of passion about this language and am very grateful for the effort that has gone into making it what it is and will continue to make it great in the future. I applaud the decision that made this process a bit more public than it has been in the past and apologize for the grief that these forums impose of those who work on the front lines. :)
I think this feature is being thought independently of the scenario. It's people (me included) that is complaining about the scenarios it doesn't fit.
Developer
Oct 28, 2014 at 4:58 AM
Halo_Four wrote:
Because nameof() is so cross-cutting you are designing it scenario-by-scenario whether you want to or not because there are a ton of different scenarios in which it can be employed syntactically and you have to evaluate each of the results independently.
We use the use cases to validate that a design works as expected, and to help us decide when to select another design if most do not. We don't use it to guide the design for each use case independently.
Oct 28, 2014 at 6:14 AM
I'm trying to understand the power of the nameof operator. The one place in my code where it seems to fit is where I am constantly using selector expressions. Especially in ReactiveUI we do stuff like
this
    .WhenAnyValue(p => p.User.Name.FirstName )
    .Select( firstName => firstName.ToUpper)
    .Subscribe(s => Console.WriteLine(s));
WhenAnyValue takes the selector expression and parses it and chains INPC handlers down the object heirarchy finally presenting the result as an IObservable<T>.
The parsing of the selector expression however happens at runtime and involves some overhead. It most cases it does not affect application performance at all. Still I am wondering, could I use nameof to do something like
this
    .WhenAnyValue(nameof(this.User.Name.FirstName) )
    .Select( firstName => firstName.ToUpper)
    .Subscribe(s => Console.WriteLine(s));
However in the nameof examples above I don't see nameof being used with property access more than one level deep. Is this a deliberate design choice?
Oct 28, 2014 at 12:09 PM
This version looks great. Thank you!

Just to be sure:

CASE 1:
void Foo<T>(){}
void Foo<T, T1>(){}
void Test() 
{
  var t = nameof(Foo<int>); // points to Foo<T>
  var t2 = nameof(Foo); // is an error
}
CASE 2:
using C;
using C2;

static class C
{
  public static void Foo(){}
}

static class C2
{
  public static int Foo {get;set;}
}

public class C3
{
    public void Test()
    {
        var t = nameof(Foo); // is an error
    }
}
CASE 3:
public class A
{
  protected void Foo(){}
  protected static void Foo2(){}
}

public class B: A
{
  public class C
  {
    public void Bar()
    {
      var t = nameof(A.Foo2);  // points to Foo2
      var t2 = nameof(A.Foo); // is an error
    }
  }
}
Are all examples correct?
Oct 28, 2014 at 12:53 PM
bradphelan wrote:
I'm trying to understand the power of the nameof operator. The one place in my code where it seems to fit is where I am constantly using selector expressions. Especially in ReactiveUI we do stuff like
this
    .WhenAnyValue(p => p.User.Name.FirstName )
    .Select( firstName => firstName.ToUpper)
    .Subscribe(s => Console.WriteLine(s));
WhenAnyValue takes the selector expression and parses it and chains INPC handlers down the object heirarchy finally presenting the result as an IObservable<T>.
The parsing of the selector expression however happens at runtime and involves some overhead. It most cases it does not affect application performance at all. Still I am wondering, could I use nameof to do something like
this
    .WhenAnyValue(nameof(this.User.Name.FirstName) )
    .Select( firstName => firstName.ToUpper)
    .Subscribe(s => Console.WriteLine(s));
However in the nameof examples above I don't see nameof being used with property access more than one level deep. Is this a deliberate design choice?
I'd assume that even if it does parse that the result it would give you would be simply "FirstName". Since it also generates a string literal and doesn't do anything with the type of the property you'd also probably have to provide an overload of WhenAnyValue that required specification of the generic type, e.g.
string name = nameof(this.User.Name.FirstName);
this
    .WhenAnyValue<string>(name)
    .Select(firstName => firstName.ToUpper())
    .Subscribe(s => Console.WriteLine(s));
Oct 28, 2014 at 10:11 PM
Ollka wrote:
var t = nameof(Foo<int>); // points to Foo<T>
Why would it not be Foo<int>? Note that situation is very different from:
string showEnumerableTypeName<T>(T it)
{ return nameof(IEnumerable<T>); }
In that case, I would expect showEnumerableTypeName<int>(5) to return IEnumerable<T> , since the code where name() is being evaluated knows the type only as T even though the caller knows it as a IEnumerable<int>.
  var t2 = nameof(A.Foo); // is an error
I would think it should return the name of the instance method in class A if no static method exists. I don't see that requiring code to specify nameof(default(A).Foo) would be particularly helpful.
Oct 28, 2014 at 11:46 PM
Ollka wrote:
CASE 1:
void Foo<T>(){}
void Foo<T, T1>(){}
void Test() 
{
  var t = nameof(Foo<int>); // points to Foo<T>
  var t2 = nameof(Foo); // is an error
}
t would point to Foo<T> but the result would be "Foo"
t2 would be a compiler error since there is no type Foo that has no generic type parameters
CASE 2:
using C;
using C2;

static class C
{
  public static void Foo(){}
}

static class C2
{
  public static int Foo {get;set;}
}

public class C3
{
    public void Test()
    {
        var t = nameof(Foo); // is an error
    }
}
I think that would be an error, neither Foo would be in scope. If C3 inherited from either C2 or C1 then it would probably succeed and return "Foo".
CASE 3:
public class A
{
  protected void Foo(){}
  protected static void Foo2(){}
}

public class B: A
{
  public class C
  {
    public void Bar()
    {
      var t = nameof(A.Foo2);  // points to Foo2
      var t2 = nameof(A.Foo); // is an error
    }
  }
}
t would be "Foo" and I do think that t2 would be assigned "Foo" as point #1 of this proposal allows an instance member to be referenced from a type name.
Coordinator
Oct 29, 2014 at 6:51 PM
Edited Oct 29, 2014 at 7:02 PM
Ollka wrote:
This version looks great. Thank you!
Just to be sure: [snip]
Thanks Ollka. You and one of our devs simultaneously and independently highlighted the need to think about generic arguments nameof(I<A1..AK>) and nameof(E.I<A1..AK>) when I binds to a method-group. I'll update the spec to indicate, If I binds to a method-group and there are one or more generic arguments, then error "Don't provide generic arguments when taking method names".

Separately, another of our devs pointed out the case nameof(a::b) vs nameof(a::b.c). It looks like the spec already deals with that correctly: the first isn't allowed, but the second is.

(I'll also follow up with an analysis of your cases, and add them to the "examples" section).


The way I always figure out examples is to try writing an invocation statement or member access or whatever :) That way I can tell whether the "simple name / member access" forms of expression can bind to a thing. Nameof can bind to a thing if and only if normal expressions can bind to it. After that I think carefully through special cases about instance/static.
Oct 29, 2014 at 8:32 PM
lwischik wrote:
Separately, another of our devs pointed out the case nameof(a::b) vs nameof(a::b.c). It looks like the spec already deals with that correctly: the first isn't allowed, but the second is.

Do you mean that this is allowed:
nameof(System)
but this isn't:
nameof(global::System)
Is that it?
Coordinator
Oct 31, 2014 at 12:55 AM
Edited Oct 31, 2014 at 12:55 AM
PauloMorgado wrote:
Do you mean that this is allowed nameof(System) but this isn't nameof(global::System)
Yes, exactly.

We discussed the overall nameof spec at LDM on Wednesday 2014.10.29. Notes:
  • For VB, MyClass.p is also allowed
  • For VB, it should continue to allow dotting static member of an instance, and should continue to give a warning
  • For C#, it should continue to disallow dotting static member of an instance
  • For VB, the accessibility of return-types/parameters is treated differently (at a separate step) from accessibility of members. Must look into this.
  • For VB, when you write "x.f" it can be a call to x.f with implicit parentheses. Must look into this to see if it affects anything.
  • We're fine with disallowing nameof(global::System) for now. It would theoretically be nice, but its practical niceness doesn't seem to outweigh the additional spec complexity of adding it. At least up until there are howls of complaint with convincing examples where it's needed.
  • We're fine with the subset of expressions identified above.
  • For C#, the treatment of extension methods in this spec is incorrect. Name lookup only returns extension methods in VB not C#. This will require more work.
For the final point, I reckon it should work on this principle:

"If there exist some set of arguments such that I can invoke x.f<TARGS>(args), then nameof(x.f) should bind to all symbols f that can be reached by various such arguments". (that explains why shadowing is respected in C#, and why nameof works with extension methods off instances)

"And whatever symbols nameof(x.f) can bind to, then nameof(C.f) should bind to exactly the same set, where C is the type of x". (that is the cross-cutting issue that Type.instance should work, and it stops nameof from breaking when instance methods get turned into extension methods).

The trouble is that C# spec doesn't currently have any notion of name lookup for extension methods. That is only accounted-for as part of the extension method resolution spec. So it'll require legwork...
Nov 7, 2014 at 4:51 PM
lwischik wrote:
  • For C#, it should continue to disallow dotting static member of an instance
Is it error if method-group contains both static and instance methods and qualifier expression is an instance?
Nov 10, 2014 at 11:11 PM
@lwischik
Looks like the spec doesn't currently describe conflicts between the nameof and an in-scope method with that name. That is
which will only become the "nameof" operator if it doesn't already bind to a programmatic symbol named "nameof".
should be part of the specification and not the implementation notes.

Separately the value of "using foo=X.Y.Z; nameof(foo) will return "foo". " isn't clear. I see a few questions on this and other perhaps-unnecessary features above e.g. those from @Halo_Four. But I haven't seen your response. What are your thoughts? My apologies if I've missing something above.
Coordinator
Nov 11, 2014 at 12:04 AM
Ollka wrote:
[For C#, it should continue to disallow dotting static member of an instance]
Is it error if method-group contains both static and instance methods and qualifier expression is an instance?
That's a good question. I'll have to think about where that sits in the spec. What do you reckon?

We also have to drastically re-spec the C# behavior of nameof for extension methods. That's because the current name lookup rules for extension methods are a bit goofy (they just say "do namelookup for x.f as if for an invocation expression" but that's not precise enough!) I'm hoping to get this spec'd up by later this week.

dougbu wrote:
Looks like the spec doesn't currently describe conflicts between the nameof and an in-scope method with that name. Should be part of the specification and not the implementation notes.
Separately the value of "using foo=X.Y.Z; nameof(foo) will return "foo". " isn't clear. I see a few questions on this and other perhaps-unnecessary features above e.g. those from @Halo_Four. But I haven't seen your response. What are your thoughts? My apologies if I've missing something above.
Agreed that "contextual" should be part of the spec. I'll update that.

I think the questions you're referring to all are about the direction of maybe considering metadata names, or looking up names, or getting fully qualified names, or fleshing out nameof(type) into a more fully-featured feature. For picking source names e.g. "foo" in your example above, the LDM's opinion was a judgment call that the benefits of a simple language feature in this respect (one that returns the identifier of the argument as you wrote it) outweighed other concerns, and there indeed aren't even good scenarios to guide either way. As for extended the feature to also do fully-qualified names - yes it would unlock scenarios, but again it's a judgment call. Is hard to justify any additional complexity here given the reasonable and nice workaround "typeof(C) & nameof(C.f)".
Nov 11, 2014 at 4:18 PM
Edited Nov 11, 2014 at 4:26 PM
@lwischik wrote:
We also have to drastically re-spec the C# behavior of nameof for extension methods. That's because ...
Agree the base / this / plus extension methods semantics described in "IDE behavior" aren't part of "Semantics". That may make sense because the compiler mainly needs to know that an a non-empty method group exists; only the IDE cares exactly which methods are in a method group.

However at least the 5.0 C# specification doesn't clearly include extension methods in method groups. E.g. section 7.6.5.2 "Extension method invocations" doesn't mention method groups at all. Suggest expanding the existing sections rather than adding more words to the nameof section(s).
I think the questions you're referring to all are ...
Perhaps. I read them as variants of "Why include sub-feature X at all?" For example in what scenario is it useful to have a const string for an alias name?
Nov 13, 2014 at 1:31 PM
lwischik wrote:
Ollka wrote:
[For C#, it should continue to disallow dotting static member of an instance]
Is it error if method-group contains both static and instance methods and qualifier expression is an instance?
That's a good question. I'll have to think about where that sits in the spec. What do you reckon?
I would suggest do nothing in this case. If any non static candidate exists in method group.
public class C
{
  public void Foo(string s) { }
  public static void Foo() { }

  public void Test()
  {
    var result = nameof(this.Foo);
  }
}
If compiler would show an error then in all such cases it is impossible to use access of an instance , only static 'C.Foo'.
The other way is to filter out static candidates from the method group. But then if person (or tool) changes staticness of some method it could change the meaning of nameof method group.
Coordinator
Nov 13, 2014 at 4:09 PM
My opinion for methods is more or less:

Given an instance "x":

For all methods "f", if there exist a set of type arguments <T1,...> and method arguments (a1,...) such that x.f<T1,...>(a1...) binds to the method, then that method should be amongst the targets of nameof(x.f).

Given a type T:

As above, but based on whether default(T).f<T1,...>(a1...) binds to the method
Developer
Nov 13, 2014 at 5:31 PM
Edited Nov 13, 2014 at 5:31 PM
lwischik wrote:
My opinion for methods is more or less:

Given an instance "x":

For all methods "f", if there exist a set of type arguments <T1,...> and method arguments (a1,...) such that x.f<T1,...>(a1...) binds to the method, then that method should be amongst the targets of nameof(x.f).

Given a type T:

As above, but based on whether default(T).f<T1,...>(a1...) binds to the method
I agree with this as a language design principle. However the specification needs to be constructive. The "if there exists" is not directly implementable in the compiler, and cannot be evaluated by the reader of the program. We need a simple rule that can be evaluated to tell us which methods are included.
Coordinator
Nov 13, 2014 at 7:32 PM
Another TODO item:
https://roslyn.codeplex.com/workitem/370 - describe the "contextualness" rules for nameof...

It will be similar to what's described in C# spec $2.4.3 for var: "In other cases, such as with the identifier “var” in implicitly typed local variable declarations (§8.5.1), a contectual keyword can conflict with declared names. In such cases, the declared name takes precedence over the use of the identifier as a contextual keyword."
Nov 14, 2014 at 1:12 AM
lwischik wrote:
Another TODO item:
https://roslyn.codeplex.com/workitem/370 - describe the "contextualness" rules for nameof...

It will be similar to what's described in C# spec $2.4.3 for var: "In other cases, such as with the identifier “var” in implicitly typed local variable declarations (§8.5.1), a contectual keyword can conflict with declared names. In such cases, the declared name takes precedence over the use of the identifier as a contextual keyword."
Only if there's a type named var, right? Because that would be ambiguous, given the intent of keeping retro compatibility.

That's not the case with https://roslyn.codeplex.com/workitem/370