27

One of the handiest new features in C# 6 is nameof, which allows the programmer to effectively eliminate the use of magic strings.

Per the documentation, nameof returns a string:

Used to obtain the simple (unqualified) string name of a variable, type, or member.

That works just fine with explicit typing in the following code example:

string magicString = nameof(magicString);

However, when using implicit typing with the var keyword:

var magicString = nameof(magicString);

the compiler throws an error:

Cannot use local variable 'magicString' before it is declared

I then did some more experimenting with the C# Interactive window available in Visual Studio. Again, the first example worked fine, but the second example threw a different error this time:

error CS7019: Type of 'magicString' cannot be inferred since its initializer directly or indirectly refers to the definition.

The nameof expression clearly returns a string, so why can't the compiler implicitly type it when being used with the initialized variable?

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
  • 22
    To be honest, semantically speaking, the fact that `string magicString = nameof(magicString)` works bothers me more than the fact that `var magicString = nameof(magicString)` doesn't work – Kevin Gosse Mar 30 '16 at 14:04
  • 2
    Because the compiler will first initialize the variable with `default(T)` and the assigns the result of `nameof()`. – Royal Bg Mar 30 '16 at 14:04
  • 10
    The language team felt that this wasn't worth the spec complexity. https://github.com/dotnet/roslyn/issues/766 – SLaks Mar 30 '16 at 14:04
  • I think its interesting that `string magicString = nameof(magicString);` works without having `magicString` defined beforehand – Domysee Mar 30 '16 at 14:04
  • I am not an expert on this, hence this is not an answer, but is it possible that the var is not given an actual type until a variable is given to it, so as a result it has no `Name` property to be found – Alfie Goodacre Mar 30 '16 at 14:04
  • 2
    @AlfieGoodacre: That is not the way `var` works. – SLaks Mar 30 '16 at 14:04
  • 2
    @SLaks: you should post that as an answer, since it is one. Also see the linked issue ([#7031](https://github.com/dotnet/roslyn/issues/7031)). – Jeroen Mostert Mar 30 '16 at 14:07
  • @SLaks -- If you aren't going to post that as an answer, I was going to work on incorporating that into my answer. I won't do that if you're going to answer, though. – rory.ap Mar 30 '16 at 14:27

2 Answers2

23

The language team felt that this wasn't worth the spec complexity.

You can see the discussion here.

The underlying reason for this behavior is that the spec says (§8.5.1) names declared with var aren't visible in the declaring statement, since before nameof, there was no way in which that could be valid.

Implicitly typed local variable declarations are subject to the following restrictions:

  • ...
  • The initializer expression cannot refer to the declared variable itself

Without var, statements like int x = x = 1; or int x = 1, y = x; are legal; with var, nothing in that form is valid.

Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • `int x = y, y = x;` is not, in fact, legal, because `y` is used before it's declared. `int x = 1, y = x;` is legal, however (but not very remarkable). `var x = 1, y = x;` is illegal again -- not because of any references, but because multiple declarators aren't allowed with `var`, which is a separate issue. – Jeroen Mostert Mar 30 '16 at 14:58
  • @JeroenMostert: Yes; I meant `x = 1`. And, yes. – SLaks Mar 30 '16 at 16:54
  • 4
    @JeroenMostert: Indeed, a separate but related issue. When we were originally designing var the question came up, what should `var x = SomeInt(), y = SomeDouble();` mean? Should we say that `var` is replaced by `double`, which would be legal if the user actually made that replacement, or say that `x` is int and `y` is double? A poll of users showed that it was split 50-50, and so we simply made it illegal. A feature where 50% of your users think you got it wrong is probably a bad feature. – Eric Lippert Mar 30 '16 at 20:01
13

The ability to declare a variable and assign it in the same statement is syntactic sugar. For example, when you say this:

string magicString = nameof(magicString);

what you're really saying is this:

string magicString;
magicString = nameof(magicString);

Since magicString is already declared, you can use it in the next logical statement as part of the naemof operator. This is because magicString is now part of the scope that is visible to subsequent statements.

Now, the above doesn't hold true when you use var because anything that uses var to make an assignment is really all part of just one statement, not syntactic sugar for two statements like the above example. The variable magicString doesn't actually get declared until after your function call / operator / assignment, so therefore the variable is not part of the scope until it has done the assignment, i.e. in the next statement(s).

SLaks referred to the original discussion about this issue, but what is pointed out in the notes from this later C# design team meeting about this issue on the question of "Should var x = nameof(x) work?":

This works the same as with any other construct, i.e.: not. This is not a special case for nameof, and it doesn't seem worth special casing to allow it.

In other words, it's not specific to nameof.

rory.ap
  • 34,009
  • 10
  • 83
  • 174
  • 1
    Unfortunately, I don't have access to C# 6 (nameof), but if someone could help me prove this with some IL code, I'd be appreciative. – rory.ap Mar 30 '16 at 14:12
  • The way to prove this is by referring to the language spec, not IL code. IL only demonstrates what (one particular) compiler happens to do, and we already know what it happens to do in this particular case (it rejects the code). What it does in "similar" cases isn't relevant. – Jeroen Mostert Mar 30 '16 at 14:14
  • That's true, of course, I can't even compile the second one to get the IL! Der. – rory.ap Mar 30 '16 at 14:15
  • 1
    Doesnt the compiler simply replace `nameof(magicString)` with `"magicString"`? [Try Roslyn](http://tryroslyn.azurewebsites.net/#K4Zwlgdg5gBAygTxAFwKYFsDcAoADsAIwBswBjGUogQxBBgGEYBvbGNmfYsmANwHswAExgBZABQBKZq3ayUAJ0ix0VKGTjJF0GAF4YEKulR8AZmJVrSGrVAk5Zs+nwgg+RVADoA6orQAZSFRzVXVNJTsZNgBfbCigAA=). Not sure if thats relevant in this context though – Domysee Mar 30 '16 at 14:17
  • @Domysee -- It can only do that once it has a valid declaration which it obviously doesn't have yet until *after* the `nameof` call succeeds. It's a catch 22. – rory.ap Mar 30 '16 at 14:19
  • 1
    @JeroenMostert -- Actually, what I really want to prove is how `nameof` works in *correct* code, i.e. what IL is produced by a statement that correctly uses `nameof`, e.g. `var x = "hello"; var y = nameof(x)`. That would help to elucidate the "odd" behavior. – rory.ap Mar 30 '16 at 14:21
  • 1
    `nameof` isn't a call but an operator, and the type of any `nameof` expression is known (`string`), regardless of its operand. It is certainly possible for the compiler to accept `var magicString = nameof(magicString)`, the implementers just decided not to accommodate this case. – Jeroen Mostert Mar 30 '16 at 14:21
  • Your analogy is fine, and a reasonable justification for why this code "shouldn't" work, even on the C# level. You don't need to drill down to IL for this, if only because "nameof" is a compile-time only construct with no existence as IL at all. – Jeroen Mostert Mar 30 '16 at 14:24
  • @JeroenMostert -- You're right, I've clarified my answer. I'm still interested to see the IL for working code, because I have a feeling the declaration doesn't happen until after the assignment portion when using a `var`. – rory.ap Mar 30 '16 at 14:24
  • Declarations in IL don't happen "after" or "before" at all. Instead, every method has a list of locals. Likewise, `var` is a compile-time only construct; the compiler needs to figure out the type before the local can be added to the list of declarations at all. – Jeroen Mostert Mar 30 '16 at 14:27
  • so what happens with `dynamic`? presumably that is okay. – Meirion Hughes Mar 30 '16 at 14:28
  • @MeirionHughes: yes, `dynamic magicString = nameof(magicString)` compiles just fine, just like `string magicString` does. So does `object magicString`, for that matter. The problem is `var`. – Jeroen Mostert Mar 30 '16 at 14:34