Implicit and explicit typing with C# 6 nameof
Asked Answered
H

2

28

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?

Honeybunch answered 30/3, 2016 at 14:0 Comment(8)
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 workClow
Because the compiler will first initialize the variable with default(T) and the assigns the result of nameof().Criner
The language team felt that this wasn't worth the spec complexity. github.com/dotnet/roslyn/issues/766Grum
I think its interesting that string magicString = nameof(magicString); works without having magicString defined beforehandSchwab
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 foundUntraveled
@AlfieGoodacre: That is not the way var works.Grum
@SLaks: you should post that as an answer, since it is one. Also see the linked issue (#7031).Forth
@Grum -- 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.Brickwork
G
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.

Grum answered 30/3, 2016 at 14:31 Comment(3)
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.Forth
@JeroenMostert: Yes; I meant x = 1. And, yes.Grum
@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.Remuneration
B
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.

Brickwork answered 30/3, 2016 at 14:6 Comment(12)
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.Brickwork
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.Forth
That's true, of course, I can't even compile the second one to get the IL! Der.Brickwork
Doesnt the compiler simply replace nameof(magicString) with "magicString"? Try Roslyn. Not sure if thats relevant in this context thoughSchwab
@Schwab -- 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.Brickwork
@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.Brickwork
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.Forth
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.Forth
@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.Brickwork
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.Forth
so what happens with dynamic? presumably that is okay.Saracen
@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.Forth

© 2022 - 2024 — McMap. All rights reserved.