nameof() 是在编译时评估的吗?

Posted

技术标签:

【中文标题】nameof() 是在编译时评估的吗?【英文标题】:Is nameof() evaluated at compile-time? 【发布时间】:2014-12-21 18:40:28 【问题描述】:

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

这是在编译时评估,还是在运行时通过某些 Roslyn API 评估?

【问题讨论】:

Roslyn 是新的编译器平台。它只在编译时使用。 @PauloMorgado 这不是真的,您可以在运行时使用 Rosyln 来做事。比如构建一个实时代码编辑器或使用 Rosyln 的解析东西来处理树或表达式之类的事情 @ChrisMarisic 这是我的印象,但我没有回应,因为我对该主题的了解有限(因此我的问题)。我确实遇到过这个:scriptcs.net,这是 Roslyn 强大功能的一个很好的例子,我相信它确实是运行时的东西,但我可能是错的,因为我不太了解它。 @ChrisMarisic,所以,您的意思是您可以使用 Roslyn 从源代码构建实时代码,而不是从正在运行的一个二进制文件构建。而且您仍在使用 Roslyn 将源代码转换为不会使用 Roslyn 更改这些二进制文件的二进制文件。如果您不能在运行时绝对使用 Roslyn,那么您将永远无法编译任何代码。 【参考方案1】:

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

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

来自nameof operator - v5

您可以通过this TryRoslyn example 看到这一点:

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


正如 cmets 中提到的,这意味着当您在泛型类型中的类型参数上使用 nameof 时,不要期望获得用作类型参数的实际动态类型的名称而不仅仅是类型参数的名称。所以这个:

public class Foo

    public void Bar<T>()
    
        Console.WriteLine(nameof(T));
    

Will become这个:

public class Foo

    public void Bar<T>()
    
        Console.WriteLine("T");
    

【讨论】:

这里的“编译时间”是什么?编译成 MSIL 还是编译成原生代码? @Mehrdad C# 编译器生成 IL。 快速提问,我可以在 switch case 中使用 nameof 吗? @Spell Yes【参考方案2】:

我想丰富the answer provided by @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)

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

【讨论】:

如果 MSIL 被反编译为源代码,反编译器识别它是 nameof 运算符而不是普通硬编码字符串的难易程度? 这是个好问题!如果您想获得详细的解释,您可以将其作为一个新问题发布在 SO 上.我已经验证了 ILSpy 和 Reflector 的情况。 @ADTC:由于 nameof 完全被 load-a-string-onto-the-stack 取代,反编译器怎么可能试图猜测这是一个 nameof,而不是一个简单的常量参数? @Gigi 如果你有一个反编译器,它积极地假设字符串文字来自 nameof,你可以使用它来帮助自动转换到该范例。或者这就是梦想。 返回将无法正常工作,因为nameof() 也适用于您有参考的任何内容(例如,nameof(someObject.SomeProperty) 将被转换为 "SomeProperty"。反编译应该如何找出 可靠地当它得到的只是字符串文字时,该字符串来自哪个对象?

以上是关于nameof() 是在编译时评估的吗?的主要内容,如果未能解决你的问题,请参考以下文章

sizeof 是在编译时还是运行时评估?

在引用 POM 而不是定义 POM 时评估的嵌套 Maven 属性

nameof() 到底是编译时还是运行时行为?

常量和编译时评估 - 为啥要改变这种行为

是啥阻止了这个 constexpr 函数的编译时评估?

是否可以在编译时评估数组?