132

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?

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Gigi
  • 28,163
  • 29
  • 106
  • 188
  • Roslyn is the new compiler platform. It's only used at compile-time. – Paulo Morgado Oct 27 '14 at 10:16
  • 2
    @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 something – Chris Marisic Oct 27 '14 at 20:52
  • @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: http://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. – Gigi Oct 27 '14 at 20:54
  • @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. – Paulo Morgado Oct 28 '14 at 01:35

2 Answers2

142

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");
    }
}
Palec
  • 12,743
  • 8
  • 69
  • 138
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • What is "compile-time" here? Compilation to MSIL or compilation to native code? – user541686 Oct 29 '14 at 05:36
  • 7
    @Mehrdad The C# compiler generates IL. – i3arnon Oct 29 '14 at 07:20
  • 3
    Quick question, can I use nameof in a switch case? – Spell Jul 16 '15 at 12:51
  • 2
    @Spell [Yes](http://tryroslyn.azurewebsites.net/#f:r/K4Zwlgdg5gBAygTxAFwKYFsDcAoADsAIwBswBjGUogQxBBgGEYBvbGNmfYsmANwHswAExgBZABQBKZq3ayUAJ0iweVIsFQwAvDABEInTllyA7mGSkAFjDEq1qCTKPSnT0jQ0Qq6VHwBmYkQkALkcXNno+CBA+IlQAOgB1RTQAGUhUMR0ATVQQHQlDMJcCeVQqAGtCpwBfUNrqoAA) – i3arnon Jul 16 '15 at 12:56
  • 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 = `, especially after using DotFuscator. – Leonardo Hernández Apr 23 '22 at 14:18
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.

Community
  • 1
  • 1
Faris Zacina
  • 14,056
  • 7
  • 62
  • 75
  • 5
    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? – ADTC Oct 28 '14 at 01:41
  • 11
    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. – Faris Zacina Oct 28 '14 at 08:34
  • 3
    @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? – quetzalcoatl Oct 29 '14 at 15:07
  • 2
    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. – Gigi Oct 29 '14 at 19:13
  • 2
    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 @TheMinister – Millie Smith Apr 21 '15 at 15:28
  • @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 ;) – Faris Zacina Apr 21 '15 at 16:15
  • 3
    @Gigi 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. – Caleth Jul 23 '15 at 02:20
  • 3
    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? – Adwaenyth May 12 '17 at 06:55