名字的目的是什么?

时间:2015-07-29 09:04:10

标签: c# .net c#-6.0 nameof

6.0版获得了nameof的新功能,但我无法理解它的用途,因为它只需要变量名称并在编译时将其更改为字符串。

我认为在使用<T>时可能会有一些目的,但当我尝试nameof(T)时,它只会打印一个T而不是使用的类型。

有关目的的任何想法吗?

16 个答案:

答案 0 :(得分:271)

如果要重用属性名称,例如基于属性名称抛出异常或处理PropertyChanged事件时,情况如何?在许多情况下,您希望拥有该属性的名称。

举个例子:

switch (e.PropertyName)
{
    case nameof(SomeProperty):
    { break; }

    // opposed to
    case "SomeOtherProperty":
    { break; }
}

在第一种情况下,重命名SomeProperty也会更改属性的名称,否则会破坏编译。最后一个案例没有。

这是一种非常有用的方法来保持代码编译和无错误(排序)。

very nice article from Eric Lippert为什么infoof没有成功,而nameof却这样做了

答案 1 :(得分:157)

它对ArgumentException及其衍生物非常有用:

public string DoSomething(string input) 
{
    if(input == null) 
    {
        throw new ArgumentNullException(nameof(input));
    }
    ...

现在,如果有人重构了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....");
}

答案 2 :(得分:24)

C#6.0的nameof功能变得方便的另一个用例 - 考虑像Dapper这样的库,这使得DB检索变得更加容易。虽然这是一个很棒的库,但您需要在查询中对属性/字段名称进行硬编码。这意味着如果您决定重命名属性/字段,则很有可能忘记更新查询以使用新字段名称。使用字符串插值和nameof功能,代码变得更容易维护和类型安全。

从链接

中给出的示例

没有名字

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });

,名称为

var dog = connection.Query<Dog>($"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id", new { Age = (int?)null, Id = guid });

答案 3 :(得分:21)

您的问题已经表达了目的。您必须看到这对于记录或抛出异常可能很有用。

例如。

public void DoStuff(object input)
{
    if (input == null)
    {
        throw new ArgumentNullException(nameof(input));
    }
}

这很好,如果我更改代码将会中断的变量名称或者返回带有错误消息的异常

当然,用途并不仅限于这种简单的情况。只要对变量或属性的名称进行编码就可以使用nameof

当您考虑各种绑定和反射情况时,用途是多方面的。它是将运行时错误带入编译时的绝佳方法。

答案 4 :(得分:12)

我能想到的最常见用例是使用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的属性/参数。

请注意,某些框架使用一些反射魔法来获取属性的名称,但现在我们已经不再需要这个名称了

答案 5 :(得分:8)

最常见的用法是输入验证,例如

//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# and Visual Basic Reference)

答案 6 :(得分:6)

正如其他人已经指出的那样,nameof运算符会在源代码中插入元素的名称。

我想补充说,这在重构方面是一个非常好的主意,因为它使这个字符串重构安全。以前,我使用静态方法将反射用于相同目的,但这会影响运行时性能。 nameof运算符没有运行时性能影响;它在编译时完成它的工作。如果您查看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运算符。

答案 7 :(得分:6)

ASP.NET Core MVC项目使用AccountController.cs中的nameofManageController.cs RedirectToAction方法来引用控制器中的操作。

示例:

return RedirectToAction(nameof(HomeController.Index), "Home");

这转换为:

return RedirectToAction("Index", "Home");

并将用户带到&#39;索引&#39; “家庭&#39;中的行动控制器,即/Home/Index

答案 8 :(得分:5)

考虑您在代码中使用变量并需要获取变量的名称并让我们说打印它,您应该使用

plink.exe -ssh username@1.2.3.4 -P 22 -pw mypass /ip hotspot user add limit-uptime=1h server=all name=user1 password=user1

如果有人重构代码并使用“myVar”的另一个名称,他/她将不得不在代码中查看字符串值并相应地改变它。

相反,如果你有

int myVar = 10;
print("myVar" + " value is " + myVar.toString());

自动重构会有所帮助!

答案 9 :(得分:4)

The MSDN article 列出了其他几个MVC路由(真正点击这个概念的示例)。 (格式化)描述段落如下:

  
      
  • 报告代码中的错误时
  •   
  • 连接模型 - 视图 - 控制器(MVC)链接,
  •   
  • 触发属性更改事件等,
  •   
     你经常想要   捕获方法的字符串名称。使用nameof可以帮助保留您的代码   重命名定义时有效。

     

必须使用字符串文字之前   在重命名代码元素时引用定义哪个是脆弱的   因为工具不知道检查这些字符串文字。

已接受/最受好评的答案已经提供了几个非常好的具体例子。

答案 10 :(得分:3)

nameof运算符的目的是提供工件的源名称。

通常,源名称与元数据名称相同:

public void M(string p)
{
    if (p == null)
    {
        throw new ArgumentNullException(nameof(p));
    }
    ...
}

public int P
{
    get
    {
        return p;
    }
    set
    {
        p = value;
        NotifyPropertyChanged(nameof(P));
    }
}

但情况可能并非总是如此:

using i = System.Int32;
...
Console.WriteLine(nameof(i)); // prints "i"

或者:

public static string Extension<T>(this T t)
{
    return nameof(T); returns "T"
}

我给它的一个用途是命名资源:

[Display(
    ResourceType = typeof(Resources),
    Name = nameof(Resources.Title_Name),
    ShortName = nameof(Resources.Title_ShortName),
    Description = nameof(Resources.Title_Description),
    Prompt = nameof(Resources.Title_Prompt))]

事实是,在这种情况下,我甚至不需要生成的属性来访问资源,但现在我有一个编译时检查资源是否存在。

答案 11 :(得分:1)

我发现nameof提高了应用程序中非常长且复杂的SQL语句的可读性。它使变量脱颖而出,并省去了弄清楚变量在SQL语句中使用位置的工作。

public bool IsFooAFoo(string foo, string bar)
{
    var aVeryLongAndComplexQuery = $@"SELECT yada, yada
    -- long query in here
    WHERE fooColumn = @{nameof(foo)}
    AND barColumn = @{nameof(bar)}
    -- long query here";


    SqlParameter[] parameters = {
        new SqlParameter(nameof(foo), SqlDBType.VarChar, 10){ Value = foo },
        new SqlParameter(nameof(bar), SqlDBType.VarChar, 10){ Value = bar },
    }
}

答案 12 :(得分:0)

nameof关键字的一个用途是在wpf 中以编程方式设置Binding

要设置Binding,您必须使用字符串设置Path,并使用nameof关键字,可以使用重构选项。

例如,如果您的IsEnable中有UserControl依赖项属性,并且您希望将其绑定到IsEnable中某些CheckBox的{​​{1}},你可以使用这两个代码:

UserControl

CheckBox chk = new CheckBox();
Binding bnd = new Binding ("IsEnable") { Source = this };
chk.SetBinding(IsEnabledProperty, bnd);

很明显,第一个代码无法重构,但是第一个代码才能重构......

答案 13 :(得分:0)

以前我们使用的是这样的东西:

// 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()。

答案 14 :(得分:0)

使用ASP.Net MVC时有优势。当您使用HTML帮助程序在视图中构建某个控件时,它使用html输入的名称属性中的属性名称:

@Html.TextBoxFor(m => m.CanBeRenamed)

它是这样的:

<input type="text" name="CanBeRenamed" />

现在,如果您需要在Validate方法中验证属性,可以执行以下操作:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
  if (IsNotValid(CanBeRenamed)) {
    yield return new ValidationResult(
      $"Property {nameof(CanBeRenamed)} is not valid",
      new [] { $"{nameof(CanBeRenamed)}" })
  }
}

如果您使用重构工具重命名属性,则不会破坏您的验证。

答案 15 :(得分:0)

nameof的另一个用例是检查标签页,而不是检查索引,您可以按如下方式检查标签页的Name属性:

if(tabControl.SelectedTab.Name == nameof(tabSettings))
{
    // Do something
}

少混乱:)