6.0版获得了nameof的新功能,但我不能理解它的目的,因为它只是接受变量名并在编译时将其更改为字符串。
我认为它在使用<T>时可能有一些目的,但当我尝试命名(T)时,它只是打印我一个T而不是使用的类型。
知道目的吗?
6.0版获得了nameof的新功能,但我不能理解它的目的,因为它只是接受变量名并在编译时将其更改为字符串。
我认为它在使用<T>时可能有一些目的,但当我尝试命名(T)时,它只是打印我一个T而不是使用的类型。
知道目的吗?
当前回答
最常见的用法是在输入验证中,例如
//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参考)
其他回答
正如其他人已经指出的那样,操作符的名称确实插入了源代码中给出的元素名称。
我想补充一点,这在重构方面是一个非常好的想法,因为它使得字符串重构是安全的。以前,我使用了一个静态方法,它利用反射来达到同样的目的,但这对运行时性能有影响。操作符的名称对运行时性能没有影响;它在编译时完成它的工作。如果查看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…
在运行时确定名称会损失一些性能,但对于混淆是安全的。如果混淆既不需要也不计划,我建议使用操作符的名称。
最常见的用法是在输入验证中,例如
//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参考)
如果您想重用属性名,例如根据属性名抛出异常,或者处理PropertyChanged事件,该怎么办呢?在许多情况下,您都希望拥有属性的名称。
举个例子:
switch (e.PropertyName)
{
case nameof(SomeProperty):
{ break; }
// opposed to
case "SomeOtherProperty":
{ break; }
}
在第一种情况下,如果不同时更改属性定义和nameof(SomeProperty)表达式,则重命名SomeProperty将导致编译错误。在第二种情况下,重命名SomeOtherProperty或更改“SomeOtherProperty”字符串将导致无声地破坏运行时行为,在构建时没有错误或警告。
这是一种非常有用的方法,可以保持代码的编译和(在某种程度上)没有错误。
(Eric Lippert写了一篇很好的文章,为什么infoof没有入选,而nameof却入选了)
我能想到的最常见的用例是使用INotifyPropertyChanged接口时。(基本上所有与WPF和绑定相关的东西都使用这个接口)
看看这个例子:
public class Model : INotifyPropertyChanged
{
// From the INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
private string foo;
public String Foo
{
get { return this.foo; }
set
{
this.foo = value;
// Old code:
PropertyChanged(this, new PropertyChangedEventArgs("Foo"));
// New Code:
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo)));
}
}
}
正如您可以看到的,在旧的方法中,我们必须传递一个字符串来指示哪个属性已经更改。使用nameof,我们可以直接使用属性的名称。这可能看起来没什么大不了的。但是想象一下如果有人改变了属性Foo的名字会发生什么。当使用字符串时,绑定将停止工作,但编译器不会警告你。当使用nameof时,你会得到一个编译器错误,没有名称Foo的属性/参数。
请注意,有些框架使用一些反射魔法来获取属性的名称,但现在我们不再需要nameof this了。
c# 6.0的nameof特性的另一个用例变得很方便——考虑像Dapper这样的库,它使数据库检索变得更容易。虽然这是一个很棒的库,但您需要在查询中硬编码属性/字段名。这意味着如果你决定重命名你的属性/字段,你很有可能会忘记更新查询来使用新的字段名。使用字符串插值和特性名称,代码变得更容易维护和类型安全。
从链接中给出的例子
没有nameof
var dog = connection.Query<Dog>(
"select Age = @Age, Id = @Id",
new {Age = (int?) null, Id = guid});
与nameof
var dog = connection.Query<Dog>(
$"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id",
new {Age = (int?) null, Id = guid});