nameof 和 typeof 的区别
Difference between nameof and typeof
如果我错了请纠正我,但是做一些类似
的事情
var typeOfName = typeof(Foo).Name;
和
var nameOfName = nameof(Foo);
应该会给你完全相同的输出。根据此来源,可以理解的原因之一:https://msdn.microsoft.com/en-us/library/dn986596.aspx 是
"Using nameof helps keep your code valid when renaming definitions"
如果您想将 class 实例作为字符串获取,不可能 做类似的事情:
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 });
在这两种情况下(typeof
和 nameof
)都可以进行重构,所以我看不出有任何其他理由重新发明另一个更高级别的关键字,例如 nameof
,以执行已经存在的东西。它们之间有什么我看不清楚的区别吗?
最后,如果有人能为我指出一个参考源以查看 nameof
的实现,我将不胜感激。它使用反射吗?
更新 1:
摘自 here
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
变成编译时常量。 typeof(...).Name
需要一点思考。它并过分昂贵,但在某些情况下可能会造成伤害。
其次,它用于类型名称以外的其他用途。例如,参数:
void SomeMethod(int myArgument)
{
Debug.WriteLine(nameof(myArgument));
}
您还可以获得 class 成员甚至当地人的名字。不用说,这对于调试信息是相当有用的。这也是实现不那么脆弱的反射的方法之一,例如。解析表达式树(遗憾的是,在我要使用它的项目中,我们仍然停留在使用 C# 5 的 .NET 4.0 上——它会在这里和那里为我节省一些技巧)。
为了消除一些混淆,nameof
不是 函数,typeof
也不是。它是一个编译时运算符,它总是在编译时求值(尽管很明显,泛型在时间上将 "compile-time" 移得更远)。
Typeof returns 类型对象。它通常用作参数或用作变量或字段。 typeof 运算符是获取类型指针的表达式的一部分。
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
}
}
输出
System.Char
System.Int32
System.Byte
System.IO.Stream
System.IO.TextWriter
System.Array
System.Int32[]
Nameof 同时,returns 一个带有变量名称的字符串。它在编译时工作。它是一种特殊的编译器功能,可以简化一些程序。
int size=100;
Console.WriteLine(nameof(size));
输出:尺寸
使用反射生成字符串是可能的,但不是很优雅,而且并不总是可行。例如,您不能在沙盒代码中使用反射。而且你不能在局部变量上使用它。而且很贵
nameof
运算符在编译时起作用。编译器在解析代码时已经知道名称。所以可以简单地生成字符串文字。非常快,不能再快了,而且没有运行时限制。
Typeof returns 类型对象。它通常用作参数或用作变量或字段。 typeof 运算符是获取类型指针的表达式的一部分。
Nameof,同时,returns一个带有变量名的字符串。它在编译时工作。它是一种特殊的编译器功能,可以简化一些程序。
它们之间有一些区别,但主要是背后的实际原因。
示例 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)));
Used to obtain the simple (unqualified) string name of a variable, type, or member.
...
The argument to nameof must be a simple name, qualified name, member access, base access with a specified member, or this access with a specified member. The argument expression identifies a code definition, but it is never evaluated.
Because the argument needs to be an expression syntactically, there are many things disallowed that are not useful to list. The following are worth mentioning that produce errors: predefined types (for example, int or void), nullable types (Point?
), array types (Customer[,]
), pointer types (Buffer*
), qualified alias (A::B
), and unbound generic types (Dictionary<,>
), preprocessing symbols (DEBUG
), and labels (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
这是使用 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 |
如果我错了请纠正我,但是做一些类似
的事情var typeOfName = typeof(Foo).Name;
和
var nameOfName = nameof(Foo);
应该会给你完全相同的输出。根据此来源,可以理解的原因之一:https://msdn.microsoft.com/en-us/library/dn986596.aspx 是
"Using nameof helps keep your code valid when renaming definitions"
如果您想将 class 实例作为字符串获取,不可能 做类似的事情:
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 });
在这两种情况下(typeof
和 nameof
)都可以进行重构,所以我看不出有任何其他理由重新发明另一个更高级别的关键字,例如 nameof
,以执行已经存在的东西。它们之间有什么我看不清楚的区别吗?
最后,如果有人能为我指出一个参考源以查看 nameof
的实现,我将不胜感激。它使用反射吗?
更新 1: 摘自 here
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
变成编译时常量。 typeof(...).Name
需要一点思考。它并过分昂贵,但在某些情况下可能会造成伤害。
其次,它用于类型名称以外的其他用途。例如,参数:
void SomeMethod(int myArgument)
{
Debug.WriteLine(nameof(myArgument));
}
您还可以获得 class 成员甚至当地人的名字。不用说,这对于调试信息是相当有用的。这也是实现不那么脆弱的反射的方法之一,例如。解析表达式树(遗憾的是,在我要使用它的项目中,我们仍然停留在使用 C# 5 的 .NET 4.0 上——它会在这里和那里为我节省一些技巧)。
为了消除一些混淆,nameof
不是 函数,typeof
也不是。它是一个编译时运算符,它总是在编译时求值(尽管很明显,泛型在时间上将 "compile-time" 移得更远)。
Typeof returns 类型对象。它通常用作参数或用作变量或字段。 typeof 运算符是获取类型指针的表达式的一部分。
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
}
}
输出
System.Char
System.Int32
System.Byte
System.IO.Stream
System.IO.TextWriter
System.Array
System.Int32[]
Nameof 同时,returns 一个带有变量名称的字符串。它在编译时工作。它是一种特殊的编译器功能,可以简化一些程序。
int size=100;
Console.WriteLine(nameof(size));
输出:尺寸
使用反射生成字符串是可能的,但不是很优雅,而且并不总是可行。例如,您不能在沙盒代码中使用反射。而且你不能在局部变量上使用它。而且很贵
nameof
运算符在编译时起作用。编译器在解析代码时已经知道名称。所以可以简单地生成字符串文字。非常快,不能再快了,而且没有运行时限制。
Typeof returns 类型对象。它通常用作参数或用作变量或字段。 typeof 运算符是获取类型指针的表达式的一部分。
Nameof,同时,returns一个带有变量名的字符串。它在编译时工作。它是一种特殊的编译器功能,可以简化一些程序。
它们之间有一些区别,但主要是背后的实际原因。 示例 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)));
Used to obtain the simple (unqualified) string name of a variable, type, or member.
...
The argument to nameof must be a simple name, qualified name, member access, base access with a specified member, or this access with a specified member. The argument expression identifies a code definition, but it is never evaluated.
Because the argument needs to be an expression syntactically, there are many things disallowed that are not useful to list. The following are worth mentioning that produce errors: predefined types (for example, int or void), nullable types (
Point?
), array types (Customer[,]
), pointer types (Buffer*
), qualified alias (A::B
), and unbound generic types (Dictionary<,>
), preprocessing symbols (DEBUG
), and labels (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
这是使用 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 |