是否在编译时评估nameof()?


114

在C#6中,可以使用nameof()运算符获取包含变量名或类型名的字符串。

是否在编译时或通过某些Roslyn API在运行时评估?


Roslyn是新的编译器平台。它仅在编译时使用。
Paulo Morgado 2014年

2
@PauloMorgado并非如此,您可以在运行时使用Rosyln进行操作。例如构建实时代码编辑器或使用Rosyln的解析内容来处理树或表达式等内容
Chris Marisic 2014年

@ChrisMarisic这是我的印象,但是由于我对该主题的了解有限(因此提出了问题),我没有回应。我确实碰到过这样的情况:scriptcs.net是Roslyn力量的一个很好的例子,并且我相信它是运行时的东西,但是我可能是错的,因为我对此不太了解。
Gigi 2014年

@ChrisMarisic,所以,您要说的是您可以使用Roslyn从源代码而不是从运行的一个二进制文件中构建实时代码。而且您仍在使用Roslyn将源转换为不会使用Roslyn更改二进制代码的二进制文件。如果您不能在运行时精打细算地使用Roslyn,那么您将永远无法编译任何代码。
Paulo Morgado 2014年

Answers:


119

是。nameof()在编译时评估。查看规格的最新版本:

表达式的名称是一个常量。在所有情况下,在编译时都会 nameof(...)进行求值以生成字符串。它的参数不会在运行时求值,并且被视为不可访问的代码(但是不会发出“不可访问的代码”警告)。

来自nameof运算符-v5

您可以通过TryRoslyn示例看到这一点,其中:

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

被编译并反编译为:

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

其等效运行时间为:

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

如注释中所提到的,这意味着当您nameof在泛型类型中使用类型参数时,不要指望将实际动态类型的名称用作类型参数,而不仅仅是类型参数的名称。所以这:

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

会变成这样:

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

什么是“编译时间”?编译成MSIL还是编译成本机代码?
user541686 2014年

6
@Mehrdad C#编译器生成IL。
i3arnon 2014年

3
快速提问,我可以在开关盒中使用nameof吗?
拼写

2
@Spell
i3arnon

58

我想丰富@ I3arnon提供的答案,以证明它是在编译时评估的。

假设我想使用nameof运算符在控制台中打印变量的名称:

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

当您签出生成的MSIL时,您会看到它等同于字符串声明,因为使用ldstr操作符将对字符串的对象引用压入堆栈:

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)

您会注意到,声明名字字符串并使用nameof运算符会在MSIL中生成相同的代码,这意味着nameof与声明字符串变量一样有效。


4
如果将MSIL反编译为源代码,那么反编译器将容易地认识到它是一个nameof运算符,而不是一个简单的硬编码字符串吗?
ADTC 2014年

11
这是个好问题!您可以将其作为SO上的新问题发布,如果您想获得详细的说明:) ..然而,简短的答案是,反编译器将无法确定它是nameof运算符,而是使用字符串文字代替。我已经验证了ILSpy和Reflector的情况。
Faris Zacina 2014年

2
@ADTC:由于nameof已完全被加载到堆栈上的字符串所取代,反编译器甚至如何猜测这是nameof而不是简单的常量参数?
quetzalcoatl 2014年

2
这太有趣了。也许反编译器可以根据当前上下文(您所在的方法/属性/等的名称)检查字符串。尽管如此,它仍然不可能100%可靠-毕竟您可能使用了硬编码字符串。
Gigi 2014年

2
尽管我同意您在编译后不知道它是否为nameof,但我看不到任何迹象表明ILSpy或Reflector支持C#6。如果是这样,您将无法对其进行测试@TheMinister
Millie Smith
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.