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

Gig*_*igi 112 c# roslyn c#-6.0

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

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

i3a*_*non 117

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

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

运营商名称 - v5

您可以通过此TryRoslyn示例看到:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(nameof(Foo));
    }
}
Run Code Online (Sandbox Code Playgroud)

编译并反编译成:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine("Foo");
    }
}
Run Code Online (Sandbox Code Playgroud)

它的运行时间相当于:

public class Foo
{
    public void Bar()
    {
        Console.WriteLine(typeof(Foo).Name);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine(nameof(T));
    }
}
Run Code Online (Sandbox Code Playgroud)

会变成这样的:

public class Foo
{
    public void Bar<T>()
    {
        Console.WriteLine("T");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Mehrdad C#编译器生成IL. (6认同)
  • 快问,我可以在开关盒中使用nameof吗? (3认同)
  • @Spell [是](http://tryroslyn.azurewebsites.net/#f:r/K4Zwlgdg5gBAygTxAFwKYFsDcAoADsAIwBswBjGUogQxBBgGEYBvbGNmfYsmANwHswAExgBZABQBKZq3ayUAJ0iweVIsFQwAvDABEInTllyA7mGSkAFjDEq1qCTKPSnT0jQ0Qq6VHwBmYkQkALkcXNno+CBA+IlQAOgB1RTQAGUhUMR0ATVQQHQlDMJcCeVQqAGtCpwBfUNrqoAA) (2认同)

Far*_*ina 57

我希望丰富@ I3arnon提供的答案,并证明它是在编译时进行评估的.

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

 var firstname = "Gigi";
 var varname = nameof(firstname);
 Console.WriteLine(varname); // Prints "firstname" to the console
Run Code Online (Sandbox Code Playgroud)

当您检查生成的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)
Run Code Online (Sandbox Code Playgroud)

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

  • 这是个好问题!如果你想得到一个详细的解释,你可以把它作为一个新的问题发布在那里:)但是简短的答案是反编译器无法弄清楚它是一个名称操作符,但是会使用字符串文字而不是.我已经验证了ILSpy和Reflector的情况. (11认同)
  • 如果MSIL被反编译为源代码,反编译器识别它的容易程度是`nameof`运算符,而不是简单的硬编码字符串? (4认同)
  • 返回并不完全有效,因为`nameof()` 也适用于您引用的任何内容(例如`nameof(someObject.SomeProperty)` 将被翻译为仅`"SomeProperty"`。应该如何反编译找出**可靠地**当它得到的只是字符串文字时,该字符串来自哪个对象? (3认同)
  • @ADTC:由于nameof完全替换为load-a-string-on-the-stack,反编译器甚至可以尝试猜测这是一个名称,而不是一个简单的常量参数? (2认同)
  • 这太有趣了.也许反编译器可以根据当前上下文检查字符串(您所在的方法/属性/等的名称).尽管如此,它仍然无法100%可靠 - 毕竟你可能已经使用了硬编码字符串. (2认同)
  • 虽然我同意您在编译后无法知道它是否是一个名称,但我没有看到ILSpy或Reflector支持C#6的任何迹象.如果是这样,你就无法测试它@TheMinister (2认同)
  • @Gigi如果您有一个反编译器,积极地假设字符串文字来自nameof,您可以使用它来帮助自动转换到该范例.或者这就是梦想. (2认同)