6.0版获得了nameof的新功能,但我不能理解它的目的,因为它只是接受变量名并在编译时将其更改为字符串。
我认为它在使用<T>时可能有一些目的,但当我尝试命名(T)时,它只是打印我一个T而不是使用的类型。
知道目的吗?
6.0版获得了nameof的新功能,但我不能理解它的目的,因为它只是接受变量名并在编译时将其更改为字符串。
我认为它在使用<T>时可能有一些目的,但当我尝试命名(T)时,它只是打印我一个T而不是使用的类型。
知道目的吗?
当前回答
正如其他人已经指出的那样,操作符的名称确实插入了源代码中给出的元素名称。
我想补充一点,这在重构方面是一个非常好的想法,因为它使得字符串重构是安全的。以前,我使用了一个静态方法,它利用反射来达到同样的目的,但这对运行时性能有影响。操作符的名称对运行时性能没有影响;它在编译时完成它的工作。如果查看MSIL代码,就会发现嵌入了字符串。请参阅下面的方法及其反汇编代码。
static void Main(string[] args)
{
Console.WriteLine(nameof(args));
Console.WriteLine("regular text");
}
// striped nops from the listing
IL_0001 ldstr args
IL_0006 call System.Void System.Console::WriteLine(System.String)
IL_000C ldstr regular text
IL_0011 call System.Void System.Console::WriteLine(System.String)
IL_0017 ret
然而,如果您打算让软件变得模糊,这可能是一个缺点。经过混淆处理后,嵌入的字符串可能不再匹配元素的名称。依赖于此文本的机制将会崩溃。例如,包括但不限于:Reflection, NotifyPropertyChanged…
在运行时确定名称会损失一些性能,但对于混淆是安全的。如果混淆既不需要也不计划,我建议使用操作符的名称。
其他回答
正如其他人已经指出的那样,操作符的名称确实插入了源代码中给出的元素名称。
我想补充一点,这在重构方面是一个非常好的想法,因为它使得字符串重构是安全的。以前,我使用了一个静态方法,它利用反射来达到同样的目的,但这对运行时性能有影响。操作符的名称对运行时性能没有影响;它在编译时完成它的工作。如果查看MSIL代码,就会发现嵌入了字符串。请参阅下面的方法及其反汇编代码。
static void Main(string[] args)
{
Console.WriteLine(nameof(args));
Console.WriteLine("regular text");
}
// striped nops from the listing
IL_0001 ldstr args
IL_0006 call System.Void System.Console::WriteLine(System.String)
IL_000C ldstr regular text
IL_0011 call System.Void System.Console::WriteLine(System.String)
IL_0017 ret
然而,如果您打算让软件变得模糊,这可能是一个缺点。经过混淆处理后,嵌入的字符串可能不再匹配元素的名称。依赖于此文本的机制将会崩溃。例如,包括但不限于:Reflection, NotifyPropertyChanged…
在运行时确定名称会损失一些性能,但对于混淆是安全的。如果混淆既不需要也不计划,我建议使用操作符的名称。
nameof的目的是重构。例如,当你在代码中其他地方通过nameof引用一个类的名字时,你会得到一个编译错误,这是你想要的。如果你没有使用nameof,只有一个普通的字符串作为引用,你必须全文搜索类的名称才能更改它。那是一种痛苦。有了nameof,您就可以轻松地在IDE中自动构建和获取所有需要更改的用例。
它对ArgumentException及其衍生物非常有用:
public string DoSomething(string input)
{
if(input == null)
{
throw new ArgumentNullException(nameof(input));
}
...
现在,如果有人重构输入参数的名称,异常也将保持最新。
在以前必须使用反射来获取属性或参数名称的某些地方,它也很有用。
在你的例子中,nameof(T)获取类型参数的名称-这也很有用:
throw new ArgumentException(nameof(T), $"Type {typeof(T)} does not support this method.");
nameof的另一种用法是用于枚举——通常如果你想要枚举的字符串名称,你可以使用.ToString():
enum MyEnum { ... FooBar = 7 ... }
Console.WriteLine(MyEnum.FooBar.ToString());
> "FooBar"
这实际上相对较慢,因为. net保存枚举值(即7)并在运行时查找名称。
用nameof代替:
Console.WriteLine(nameof(MyEnum.FooBar))
> "FooBar"
现在。net在编译时将枚举名称替换为字符串。
还有一种用法是INotifyPropertyChanged和日志记录——在这两种情况下,你都想把你调用的成员的名字传递给另一个方法:
// Property with notify of change
public int Foo
{
get { return this.foo; }
set
{
this.foo = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.Foo));
}
}
还是……
// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
Log(nameof(DoSomething), "Message....");
}
之前我们使用的是这样的东西:
// Some form.
SetFieldReadOnly( () => Entity.UserName );
...
// Base form.
private void SetFieldReadOnly(Expression<Func<object>> property)
{
var propName = GetPropNameFromExpr(property);
SetFieldsReadOnly(propName);
}
private void SetFieldReadOnly(string propertyName)
{
...
}
原因-编译时安全。没有人可以默默地重命名属性并破坏代码逻辑。现在我们可以使用nameof()。
最常见的用法是在输入验证中,例如
//Currently
void Foo(string par) {
if (par == null) throw new ArgumentNullException("par");
}
//C# 6 nameof
void Foo(string par) {
if (par == null) throw new ArgumentNullException(nameof(par));
}
在第一种情况下,如果你重构了改变par参数名称的方法,你可能会忘记在ArgumentNullException中改变它。有了nameof你就不用担心了。
参见:nameof (c#和Visual Basic参考)