34

如果我错了,请纠正我,但是做类似的事情

var typeOfName = typeof(Foo).Name;

var nameOfName = nameof(Foo);

应该给你完全相同的输出。根据此来源,可以理解的原因之一是:https ://msdn.microsoft.com/en-us/library/dn986596.aspx是

“使用 nameof 有助于在重命名定义时保持代码有效”

如果您想将类实例作为字符串获取,则无法执行以下操作:

var fooInstance = new Foo();
var nameOfName = nameof(fooInstance);

但是,您可以执行以下操作:

static string GetName<T>(T item) where T : class 
{
  return typeof(T).GetProperties()[0].Name;
}
var typeOfName2 = GetName(new { fooInstance });

在这两种情况下 (typeofnameof) 都可以进行重构,因此我看不出有任何其他原因需要重新发明另一个更高级别的关键字,例如nameof,以执行已经存在的某些事情。它们之间有什么我看不清楚的区别吗?

最后,如果有人能指出一个参考源来看看nameof. 它使用反射吗?

更新 1: 取自这里

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

7 回答 7

36

两个原因:

nameof变成编译时常量。typeof(...).Name需要一点反思。它并不过分昂贵,但在某些情况下可能会受到伤害。

其次,它用于类型名称以外的其他用途。例如,参数:

void SomeMethod(int myArgument)
{
  Debug.WriteLine(nameof(myArgument));
}

您还可以获得班级成员甚至当地人的姓名。不用说,这对于调试信息非常有用。它也是在解析表达式树时实现不那么脆弱的反射的方法之一(遗憾的是,在我使用它的项目中,我们仍然坚持使用 C# 5 的 .NET 4.0 - 它可以为我节省一些技巧这里和那里)。

并澄清一些混乱,nameof不是一个功能,也不typeof。它是一个编译时运算符,并且总是在编译时进行评估(尽管很明显,泛型将“编译时”移动得更远一点)。

于 2015-11-11T11:36:10.983 回答
18

这是使用 BenchmarkDotNet 进行的基准测试

// * Summary *

Host Process Environment Information:
BenchmarkDotNet.Core=v0.9.9.0
OS=Windows
Processor=?, ProcessorCount=8
Frequency=2740584 ticks, Resolution=364.8857 ns, Timer=TSC
CLR=CORE, Arch=64-bit ? [RyuJIT]
GC=Concurrent Workstation
dotnet cli version: 1.0.0-preview2-003133

Type=GetNameBenchmark  Mode=Throughput  LaunchCount=2
WarmupCount=10  TargetCount=200

     Method |     Median |    StdDev |
----------- |----------- |---------- |
     TypeOf | 16.0348 ns | 0.7896 ns |
     NameOf |  0.0005 ns | 0.0147 ns |
于 2017-12-20T03:58:21.907 回答
10

使用反射来生成字符串是可能的,但不是很优雅,而且并不总是可行的。例如,您不能在沙盒代码中使用反射。而且你不能在局部变量上使用它。而且很贵。

运算符在nameof编译时工作。编译器在解析代码时已经知道名称。所以可以很容易地生成字符串文字。非常快,不可能更快,并且没有运行时限制。

于 2015-11-11T11:43:50.133 回答
6

它们之间有几个不同之处,但它们主要是背后的实际原因。 示例 1:

写类似的东西更优雅

switch (e.ArgumentPropertyName)
{
    case nameof(aProperty):
    break;

    case "anotherProperty":
    break;
}

尝试重构anotherProperty类型并爆炸!将nameof反映更改,"anotherProperty"将静默通过,您的代码将永远不会在该 case 语句中执行..

示例 2:

enum MetalEnum { Gold = 1,  Silver = 2, ... }

哪个更好看?

Console.WriteLine(MetalEnum.Gold.ToString()); // finds the name at runtime (slower)

或者

Console.WriteLine(nameof(MetalEnum.Gold)); // compile time (faster)

示例 3:

最后,你还记得写类似这样的东西有多难看吗

PropertyChanged(this, new PropertyChangedEventArgs("Foo"));

现在你可以写得漂亮如下:

PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));
于 2015-11-11T13:50:27.737 回答
3

Typeof returns Type objects. It is often used as a parameter or as a variable or field. The typeof operator is part of an expression that acquires the Type pointer.

class Program
{
    static Type _type = typeof(char); // Store Type as field.

    static void Main()
    {
    Console.WriteLine(_type); // Value type pointer
    Console.WriteLine(typeof(int)); // Value type
    Console.WriteLine(typeof(byte)); // Value type
    Console.WriteLine(typeof(Stream)); // Class type
    Console.WriteLine(typeof(TextWriter)); // Class type
    Console.WriteLine(typeof(Array)); // Class type
    Console.WriteLine(typeof(int[])); // Array reference type
    }
}

Output

System.Char
System.Int32
System.Byte
System.IO.Stream
System.IO.TextWriter
System.Array
System.Int32[]

Nameof, meanwhile, returns a string with a variable's name. It works at compile-time. It is a special compiler feature that simplifies some programs.

int size=100;
Console.WriteLine(nameof(size));

output:size

于 2015-11-11T11:37:19.423 回答
3

根据文档

用于获取变量、类型或成员的简单(非限定)字符串名称。

...

nameof的参数必须是简单名称、限定名称、成员访问、具有指定成员的基本访问或具有指定成员的此访问。参数表达式标识代码定义,但从不计算。

因为参数需要在语法上是一个表达式,所以有许多不允许列出的东西是无用的。以下是值得一提的产生错误的类型:预定义类型(例如,intvoid)、可空类型(Point?)、数组类型(Customer[,])、指针类型(Buffer*)、限定别名(A::B)和未绑定泛型类型(Dictionary<,>)、预处理符号(DEBUG) 和标签 ( loop:)。

nameof获得的简单名称是源名称,而不是元数据名称。

所以,这段代码:

using Integer = System.Int32;
var s = "a string";
Console.WriteLine(nameof(s));
Console.WriteLine(nameof(Integer));
Console.WriteLine(nameof(System.Int32));
void M<T>() { Console.WriteLine(nameof(T)); }
M<int>();
M<string>();

将打印:

s
Integer
Int32
T
T
于 2015-11-11T23:45:44.793 回答
0

Typeof 返回 Type 对象。它通常用作参数或变量或字段。typeof 运算符是获取 Type 指针的表达式的一部分。

同时,Nameof 返回一个带有变量名称的字符串。它在编译时工作。它是一种特殊的编译器功能,可以简化一些程序。

于 2015-11-11T11:58:40.437 回答