322

6.0 版有一个新特性nameof,但我不明白它的用途,因为它只是接受变量名并在编译时将其更改为字符串。

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

任何想法的目的?

4

17 回答 17

377

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

举个例子:

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

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

在第一种情况下,SomeProperty如果您不更改属性定义和nameof(SomeProperty)表达式,重命名将导致编译错误。在第二种情况下,重命名SomeOtherProperty或更改"SomeOtherProperty"字符串将导致运行时行为静默中断,在构建时不会出现错误或警告。

这是一种非常有用的方法,可以让您的代码保持编译和无错误(有点)。

(埃里克·利珀特( Eric Lippert)的一篇非常好的文章,为什么infoof没有成功,而nameof成功了)

于 2015-07-29T09:07:55.843 回答
204

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....");
}
于 2015-07-29T09:07:03.973 回答
29

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

例如:

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

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


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

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

于 2015-07-29T09:07:08.170 回答
28

nameofC# 6.0 的特性变得方便的另一个用例- 考虑像Dapper这样的库,它使数据库检索变得更加容易。尽管这是一个很棒的库,但您需要在查询中硬编码属性/字段名称。这意味着如果您决定重命名您的属性/字段,您很可能会忘记更新查询以使用新的字段名称。使用字符串插值和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});
于 2016-11-16T01:57:35.547 回答
14

我能想到的最常见的用例是使用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 时,您会收到一个编译器错误,即没有 name 的属性/参数Foo

请注意,一些框架使用一些反射魔法来获取属性的名称,但现在我们有了 nameof 这不再是必需的

于 2015-07-29T19:03:31.997 回答
12

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

//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 参考)

于 2015-07-29T09:09:45.377 回答
9

ASP.NET Core MVC 项目使用nameofinAccountController.csManageController.cswithRedirectToAction方法来引用控制器中的操作。

例子:

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

这转化为:

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

并将用户带到“主页”控制器中的“索引”操作,即/Home/Index

于 2016-04-22T13:55:04.353 回答
9

假设您需要在代码中打印变量的名称。如果你写:

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

然后如果有人重构代码并为 使用另一个名称myVar,他/她将不得不在您的代码中查找字符串值并相应地更改它。

相反,如果你写:

print(nameof(myVar) + " value is " + myVar.toString());

这将有助于自动重构!

于 2015-07-29T09:12:41.810 回答
7

MSDN 文章列出了 MVC 路由(对我来说真正点击了这个概念的示例)等等。(格式化的)描述段落内容如下:

  • 在报告代码错误时,
  • 连接模型-视图-控制器 (MVC) 链接,
  • 触发属性更改事件等,

您经常想要 捕获方法的字符串名称。使用 nameof 有助于在重命名定义时保持代码有效。

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

接受/评价最高的答案已经给出了几个很好的具体例子。

于 2015-08-05T16:16:54.953 回答
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运算符。

于 2015-08-01T12:36:51.910 回答
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))]

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

于 2015-07-31T00:06:25.807 回答
2

The purpose of nameof is refactoring. For example when you change the name of a class to which you refer to through nameof somewhere else in your code you will get a compilation error which is what you want. If you didn't use nameof and had just a plain string as a reference you'd have to fulltext search for the name of the class in order to change it. That's a pain in the bottom. With nameof you can rest easy, build, and get all the cases for change automatically in your IDE.

于 2021-09-02T07:48:06.350 回答
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 },
    }
}
于 2020-01-16T17:21:30.903 回答
1

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

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

不那么凌乱:)

于 2019-04-01T08:20:49.740 回答
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()。

于 2016-07-26T22:09:14.083 回答
0

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

要设置Binding您必须Path使用字符串和nameof关键字进行设置,可以使用 Refactor 选项。

例如,如果你有IsEnable依赖属性UserControl并且你想将它绑定到你IsEnable的一些CheckBoxUserControl你可以使用这两个代码:

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

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

很明显,第一个代码无法重构,但第二个代码......

于 2016-07-17T23:58:18.097 回答
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)}" })
  }
}

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

于 2018-03-20T13:43:59.063 回答