Is nameof() evaluated at compile-time?
Asked Answered
S

2

133

In C# 6, you can use the nameof() operator to get a string containing the name of a variable or a type.

Is this evaluated at compile-time, or at runtime via some Roslyn API?

Spontaneous answered 26/10, 2014 at 12:44 Comment(4)
Roslyn is the new compiler platform. It's only used at compile-time.Alfreda
@PauloMorgado that's not true, you can use Rosyln at run time to do things. Such as building a live code editor or using Rosyln's parsing stuff to do things with trees or expressions or somethingHispanic
@ChrisMarisic that is my impression, but I did not respond since my knowledge on the topic is limited (hence my question). I did come across this: scriptcs.net which is a pretty good example of Roslyn's power, and which I believe does runtime stuff, but I could be wrong as I'm not quite well-informed about it.Spontaneous
@ChrisMarisic, so, what you are sayng is that you can use Roslyn to build live code from source, not from the one binary that is running. And you're still using Roslyn to transform source into binaries that wont use Roslyn to change thos binries. If you couldn't aboslutely use Roslyn at runtime, then you could never compile any code.Alfreda
C
143

Yes. nameof() is evaluated at compile-time. Looking at the latest version of the specs:

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

From nameof operator - v5

You can see that with this TryRoslyn example where this:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(nameof(Foo));
    }
}

Is compiled and decompiled into this:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine("Foo");
    }
}

Its run-time equivalent is:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(typeof(Foo).Name);
    }
}

As was mentioned in the comments, that means that when you use nameof on type parameters in a generic type, don't expect to get the name of the actual dynamic type used as a type parameter instead of just the type parameter's name. So this:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine(nameof(T));
    }
}

Will become this:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
Circumflex answered 26/10, 2014 at 12:54 Comment(5)
What is "compile-time" here? Compilation to MSIL or compilation to native code?Griffin
@Mehrdad The C# compiler generates IL.Circumflex
Quick question, can I use nameof in a switch case?Refund
@Refund YesCircumflex
That works for type. How can you get the name of a member of the type at run time? This is useful for DataSource when indicating things like control.ValueMember = <member name>, especially after using DotFuscator.Anneal
A
62

I wanted to enrich the answer provided by @I3arnon with a proof that it is evaluated at compile-time.

Let's assume i want to print the name of a variable in the Console using the nameof operator:

 var firstname = "Gigi";
 var varname = nameof(firstname);
 Console.WriteLine(varname); // Prints "firstname" to the console

When you check out the MSIL generated you will see that it is equivalent to a string declaration because an object reference to a string gets pushed to the stack using the ldstr operator:

IL_0001: ldstr "Gigi"
IL_0006: stloc.0
IL_0007: ldstr "firstname"
IL_000c: stloc.1
IL_000d: ldloc.1
IL_000e: call void [mscorlib]System.Console::WriteLine(string)

You will notice that declaring the firstname string and using the nameof operator generates the same code in MSIL, which means nameof is as efficient as declaring a string variable.

Arterialize answered 26/10, 2014 at 18:29 Comment(8)
If the MSIL is decompiled to source code, how easy will it be for the decompiler to recognize it was a nameof operator, not a plain hardcoded string?Concavoconvex
That's a good question! you can post it as a new question on SO if you want to get a detailed explanation :).. however the short answer is that the decompiler will not be able to figure out it was a nameof operator, but will use a string literal instead. I have verified that is the case with ILSpy and Reflector.Arterialize
@ADTC: As the nameof is fully replaced with load-a-string-onto-the-stack, how could the decompiler even attempt to guess that was a nameof, and not a simple constant parameter?Lolland
That is interesting. Perhaps the decompiler could check the string against the current context (name of method/property/etc you're in). Still, there's no way for it to be 100% reliable - you might have used a hardcoded string after all.Spontaneous
While I agree that you can't know if it's a nameof after compiling, I don't see any indication that ILSpy or Reflector support C# 6 yet. If thats the case, you can't test it @TheMinisterMilter
@MillieSmith You are right about ILSpy and Reflector not officially supporting C#6, however, the MSIL emitted makes it really hard to revert it back, so i don't think C#6 support will change anything in this case. We will see ;)Arterialize
@Spontaneous if you had a decompiler that aggressively assumed string literals came from nameof you could use it to help automate a transition to that paradigm. Or such is the dream.Zalucki
Going back won't exactly work because nameof() also works on anything you have a reference on (e.g. nameof(someObject.SomeProperty) will be translated to just "SomeProperty". How should decompilation find out reliably what object that string came from when all it's got is a string literal?Mustachio

© 2022 - 2024 — McMap. All rights reserved.