Roslyn: nameof(...) behaves oddly on generic types such as Nullable<>

Created on 27 Aug 2015  路  34Comments  路  Source: dotnet/roslyn

  • nameof(Nullable) is "Nullable", as I would expect.
  • nameof(Nullable<int>) is "Nullable" - I expected "Nullable<int>".
  • nameof(Nullable<>)) is a compile-time error:

    error CS0305: Using the generic type 'Nullable' requires 1 type arguments

  • nameof(int?) is a compile-time error:

    error CS1525: Invalid expression term 'int'

  • nameof(Nullable<int>.Value) is "Value", as I would expect.
  • nameof(Nullable<>.Value) is a compile-time error:

    error CS0305: Using the generic type 'Nullable' requires 1 type arguments

  • nameof(int?.Value) is a compile-time error:

    error CS1525: Invalid expression term 'int'

Area-Language Design Bug Resolution-By Design

Most helpful comment

I would argue then that nameof(Nullable<>.Value) should also be valid

I agree. My proposal would be to augment the grammar to allow open types to appear in nameof, just as they are in typeof.

All 34 comments

nameof(int) is also a compile time error.

Yet nameof(Int32) is "Int32", and nameof(Int32?) is also a compile-time error.

Are the rules for nameof listed anywhere?

Furthermore, the VS2015 IDE wants me to simplify nameof(Nullable<int>) to nameof(int?), however doing so introduces a compile-time error as above.

Interesting. I found the "nameof(Nullable<int>) cannot be simplified" issue in DotNetAnalyzers/StyleCopAnalyzers#637, but didn't realize it affected the code fix here as well.

The detailed spec will be published in an updated language reference. These cases are all as expected.

I'm reactivating because i think nameof(Nullable<>) should work. For example, say i have:

c# class A<T> { } class A<T,U> { }

I'd like to be able to use nameof to unambiguously reference either of these. nameof(A) obviously isn't good (since i'ts ambiguous). And nameof(A<some_random_type>) is just wasted verbiage. I think we should allow open types within a nameof() expression. That way you can simply and succinctly say nameof(A<,>), and have all your features (like goto-def, refactoring, etc.) work properly. Forcing me to put in "int" as a placeholder type is just kinda ugly :)

I would argue then that nameof(Nullable<>.Value) should also be valid - otherwise it would be confusingly inconsistent. Right now it's confusing, but at least it's consistent.

@CyrusNajmabadi If you want it changed, you'll have to rewind the clock. VS2015 has already shipped with an implementation of the current spec. Nullable<> isn't syntactically a valid expression. I'll assign it to you to drive the issue with @MadsTorgersen.

I would argue then that nameof(Nullable<>.Value) should also be valid

I agree. My proposal would be to augment the grammar to allow open types to appear in nameof, just as they are in typeof.

@gafter Sounds good. I'll talk to @MadsTorgersen about what we can do here.

I would argue then that nameof(Nullable<>.Value) should also be valid

I believe @ljw1004 did _extensive_ analysis of this specific case when the nameof behavior was being finalized. He may have a link to some of those discussions with the conclusions.

Marking as a Bug to ensure that the specification is examined to ensure it properly covers the intended behavior here.

@CyrusNajmabadi, @gafter is this by design or planned to be fixed in the next version?

I have the same problem I need to use nameof like this nameof(ModelCollection<>) and nameof(ModelCollection<>.Add) where Add is method and unfortunately I can't so I'm wondering what's the current stand of the team on this?

I havne't had time to work on this further. And with how packed our schedule is for our next release, this is unlikely to make it. I still believe that we should enable this scenario. But it won't be for a while unfortunately.

@CyrusNajmabadi okay, thank you. :)

Agreed that this really should be resolved - it's bizarre to have to specify a type argument which is irrelevant.

It's worth noting that the original comment here had three aspects:

  • The invalid usage of open types in nameof
  • The invalid usage of nullable aliases (nameof(int?))
  • The result of nameof with constructed types (nameof(Nullable<int>) still returning Nullable)

The ship has sailed for the last of these - might it be worth editing the issue to only be about the first, then creating a new issue for the second?

Does "Resolution-By Design" suggest this matter is closed, btw? How do we get it reopened for the next round of design? (I would expect that allowing open types would be a non-breaking change.)

@jskeet

Does "Resolution-By Design" suggest this matter is closed, btw? How do we get it reopened for the next round of design? (I would expect that allowing open types would be a non-breaking change.)

Well, as far as I know Resolution-By Design means it works as intended but if there was nothing to discuss they would tag it with Resolution-Won't Fix or/and close the issue.

@eyalsk: Good to know, thanks :) (And it is still open...)

@jskeet To "get it opened" for the next round of design, open a new issue with a specific proposal for a specific change.

@gafter what happened with LDM regarding #4907?

Nothing, really. Should we have prioritized it over something we did in C#7?

I'm pretty sure it would be useless to allow nameof(int?), but nameof(GenericType<>) and nameof(GenericType<,>.X) are critical. In fact, they are more correct than any closed expression.

I'm pretty sure it would be useless to allow nameof(int?), but nameof(GenericType<>) and nameof(GenericType<,>.X) are critical. In fact, they are more correct than any closed expression.

I wouldn't chose nameof(GenericType<,>.X) over anything planned for C# 7.0 (or even C#7.x).

I wouldn't chose nameof(GenericType<,>.X) over anything planned for C# 7.0 (or even C#7.x).

Oddly enough, while I don't feel strongly one way or the other, I almost prefer a sense of cleanup of what we already have _before_ moving ahead with new stuff. But that's just me.

@paulomorgado Agreed for 7.0 but why not 7.x? do you know the list of things that are coming in 7.x?

I know it's a minor feature and you might not need it but this seems like a reasonable improvement for a minor version release of the language.

@eyalsk, seems like you already have it planned. 馃槃

As with everything else, what would you leave out in order to have this in?

@paulomorgado

seems like you already have it planned.

Sure! you just need to tell me what x is and I'll have it ready for you and everyone else. 馃槈

As with everything else, what would you leave out in order to have this in?

nameof was introduced in C# 6.0 what was left in order to get this in? I wonder...

@eyalsk

I don't recall all the exact details, but it has something to do with the argument of nameof needing to be an expression. And GenericTypeDefinition<> isn't one.

For the C#6 time frame, it was really just an implementation detail. I can't speak for the C# language design team, but I suspect that if someone came along with a clean PR that allowed open generics then the LDM would approve it without much hesitation.

The issue was in the C#6 language, so as to preserve backwards compatibility, we said that nameof(x) might be interpreted as an invocation of a method named nameof if one happens to be defined. Only if no suitable function is defined will it bind to the nameof operator. But parsing happens (and has to happen) prior to name look up. Therefore the parser doesn't know whether this particular nameof will be an invocation (which takes an argument) or the nameof operator (which takes the goofy stuff that's been proposed in this thread).

(In VB we said that nameof would be a new reserved keyword, end of story, backwards-compatibility be damned).

I think we implemented it in C#6 by making nameof parse its argument as an "Expression" node, which happens (in the current compiler codebase) to allow for closed generics and everything else that was needed of the nameof operator but didn't allow for open generics nor nullables.

There are fair arguments to be made on either side. (1) implementation details should never drive language design and so the feature should allow open generics no matter what the cost. (2) this is such a small niche area that it's not worth sacrificing even one jot of compiler codebase cleanliness to support it. (Personally, I sided with option 2).

As for nullables? That's a very different question. There were a bunch of difficult cases where we had to decide whether nameof would return the name that a C# language user would expect, or the name that a metadata-guru would expect...

nameof(foo<int>)   // could be "foo" or "foo`2"
nameof(int)   // could be "int" or "Integer"
nameof(MyStruct?)   // could be "MyStruct" or "Nullable" or "Nullable`1"
nameof(MyClass[])   // not really sure here

Maybe you think the nameof feature is about getting metadata names so you can write reflection code. (Unfortunately it SUCKS at this because it doesn't emit fully-qualified names). Maybe you think it's about getting names of parameters for use in ArgumentException (in which case the entire discussion in this thread is irrelevant).

I can't think of any good principle that would help us decide one way or other about how nameof should behave with nullables. And so we're better off if the language simply doesn't allow it at all. That way, someone reading your code will never be confused about what it does.

Regarding backwards compatibility, shouldn't the compiler go "ok they're using C#6 nameof" if the parameters to what might be a function invocation cannot be valid parameters for such?

i.e. if nameof(string.Empty) that could be ambiguous, but nameof(Nullable<>) is not ambiguous.

Any updates?

There will be no updates until such a time as a member of the LDM decides to champion https://github.com/dotnet/csharplang/issues/702 or an equivalent proposal.

Closing as this is no longer the repository to use for changes to the language.

Was this page helpful?
0 / 5 - 0 ratings