名字的目的是什么?

ati*_*kot 240 .net c# c#-6.0 nameof

版本6.0有一个新功能nameof,但我无法理解它的目的,因为它只需要变量名称并在编译时将其更改为字符串.

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

有意的吗?

Pat*_*man 299

如果要重用属性名称,例如基于属性名称抛出异常或处理PropertyChanged事件,则会发生什么情况.在许多情况下,您可能希望拥有该属性的名称.

举个例子:

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

    // opposed to
    case "SomeOtherProperty":
    { break; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

(来自Eric Lippert的一篇非常好的文章为什么infoof没有成功,同时nameof呢)

  • 另一个常见用例是使用`nameof`和操作名称而不是硬编码字符串在MVC中进行路由. (43认同)
  • 它有.但是Resharper和VS都没有在项目上工作.这样做.实际上,这是更好的解决方案. (6认同)
  • 我理解这一点,只是在重构名称时添加 resharper 会更改字符串,不确定 VS 是否具有类似的功能。 (2认同)
  • @sotn我不确定我明白你在问什么.没有什么可以阻止你使用它像`public class MyController {public ActionResult Index(){return View(nameof(Index)); ` - 并且你可以在非静态成员上使用`nameof`(例如你可以使用上面的类调用`nameof(MyController.Index)`并且它将发出"Index").查看https://msdn.microsoft.com/en-us/library/dn986596.aspx?f=255&MSPPError=-2147217396上的示例 (2认同)
  • 我不明白为什么那是特别的.变量名总是一样的,对吧?无论您是否有实例,变量名称都不会更改@sotn (2认同)
  • @PatrickHofman 而且我不确定我是否理解您所描述的内容。但是,如果您的意思是在非静态方法名称上使用 `nameof` 有什么特别之处呢?好吧,例如在 cshtml 文件中;我们无权访问 Controller 实例,我们通过使用 UrlHelpers 诸如 `Url.Action("Index")` 等来使用大量 url 等。因此,这意味着我们可以使用 `Url.Action(nameof(HomeController.索引))`。当我们以后重命名它并打开视图编译时,我们可以轻松地重命名动作名称.. 没有实例使用它是很大的imo.. (2认同)
  • 如果您了解这一切都是在编译时完成的,则不会。当时还没有实例,只有代码@sotn (2认同)

Kei*_*ith 166

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

public string DoSomething(string input) 
{
    if(input == null) 
    {
        throw new ArgumentNullException(nameof(input));
    }
    ...
Run Code Online (Sandbox Code Playgroud)

现在,如果有人重构input参数的名称,异常也将保持最新.

它在以前必须使用反射来获取属性或参数名称的某些地方也很有用.

在您的示例中nameof(T)获取type参数的名称 - 这也是有用的:

throw new ArgumentException(nameof(T), $"Type {typeof(T)} does not support this method.");
Run Code Online (Sandbox Code Playgroud)

nameof枚举的另一个用途是 - 通常如果你想要你使用的枚举的字符串名称.ToString():

enum MyEnum { ... FooBar = 7 ... }

Console.WriteLine(MyEnum.FooBar.ToString());

> "FooBar"
Run Code Online (Sandbox Code Playgroud)

这实际上相对较慢,因为.Net保存枚举值(即7)并在运行时查找名称.

而是使用nameof:

Console.WriteLine(nameof(MyEnum.FooBar))

> "FooBar"
Run Code Online (Sandbox Code Playgroud)

现在.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));
    }
}
Run Code Online (Sandbox Code Playgroud)

要么...

// Write a log, audit or trace for the method called
void DoSomething(... params ...)
{
    Log(nameof(DoSomething), "Message....");
}
Run Code Online (Sandbox Code Playgroud)

  • 你还有另一个很酷的功能:字符串插值! (9认同)
  • 你知道`nameOf`是否会使用`[DisplayName]`属性(如果存在)?对于`enum`示例,我经常使用`[DisplayName]`和MVC项目 (3认同)
  • @AaronLS是的,它相当专业,不是您经常使用的东西。但是,`throw new`完全是另一种反模式-我发现对于初级开发人员来说,过度使用`catch`是一个常见问题,因为感觉就像是在解决问题(大多数时候只是将其隐藏)。 (2认同)

sat*_*esh 25

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

从链接中给出的示例

没有名字

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

名字的

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

  • 我喜欢 Dapper,我真的很喜欢字符串插值,但在我看来,这看起来很丑。与如此丑陋的查询相比,通过重命名列来破坏查询的风险似乎很小。乍一看,我会说我更喜欢编写 EF LINQ 查询,或者遵循 [TableName].[ColumnName] 这样的约定,这样我可以在需要时轻松找到/替换我的查询。 (5认同)

Jod*_*ell 21

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

例如.

public void DoStuff(object input)
{
    if (input == null)
    {
        throw new ArgumentNullException(nameof(input));
    }
}
Run Code Online (Sandbox Code Playgroud)

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


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

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

  • @atikot:但是,如果重命名变量,编译器将不会注意到该字符串不再匹配. (6认同)
  • @atikot,我也是,但Resharper只生成警告,而不是编译器错误.确定性和良好建议之间存在差异. (4认同)
  • @Jodrell:而且,我怀疑,它不会检查各种其他用途 - 如何在代码隐藏中创建WPF绑定,自定义`OnPropertyChanged`方法(直接接受属性名称而不是`PropertyChangedEventArgs`),或调用反思寻找特定的成员或类型? (2认同)

Roy*_* T. 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)));           
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您在旧方式中所看到的,我们必须传递一个字符串来指示哪个属性已更改.随着nameof我们可以直接使用属性的名称.这似乎不是什么大不了的事.但想象当有人改变房产名称时会发生什么Foo.使用字符串时,绑定将停止工作,但编译器不会警告您.使用nameof时,您会收到编译器错误,指出没有名称的属性/参数Foo.

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

  • 虽然这是一种有效的方法,但更方便(和干)的方法是在新方法的参数上使用`[CallerMemberName]`属性来引发此事件. (5认同)
  • 对于`INotifyPropertyChanged`来说,这是一个'更好的在一起'的情况,使用`[CallerMemberNameAttribute]`允许从属性设置器中干净地引发更改通知,而`nameof`语法允许从不同的位置干净地引发更改通知在你的代码中. (4认同)

Mas*_*ota 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));
}
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,如果重构方法更改par参数的名称,您可能会忘记在ArgumentNullException中更改它.有了这个名字你就不用担心了.

另请参见:nameof(C#和Visual Basic参考)


bri*_*ins 7

MSDN 文章列出了 MVC 路由(真正为我点击概念的示例)以及其他几个。(格式化的)描述段落如下:

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

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

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

公认的/评分最高的答案已经给出了几个很好的具体例子。


Fre*_*red 7

在ASP.NET MVC的核心项目使用nameofAccountController.cs,并ManageController.csRedirectToAction方法在控制器中引用的动作.

例:

return RedirectToAction(nameof(HomeController.Index), "Home");
Run Code Online (Sandbox Code Playgroud)

这意味着:

return RedirectToAction("Index", "Home");
Run Code Online (Sandbox Code Playgroud)

并且take将用户带到'Home'控制器中的'Index'动作,即/Home/Index.


cel*_*arp 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
Run Code Online (Sandbox Code Playgroud)

但是,如果您计划对软件进行模糊处理,那么这可能是一个缺点.混淆后,嵌入的字符串可能不再与元素的名称匹配.依赖于此文本的机制将会中断.示例包括但不限于:Reflection,NotifyPropertyChanged ......

在运行时确定名称会降低某些性能,但对于混淆是安全的.如果既不需要也不计划混淆,我建议使用nameof运算符.


cno*_*nom 5

考虑一下你在代码中使用一个变量,并且需要获取变量的名称,然后打印它,你应该使用它

int myVar = 10;
print("myVar" + " value is " + myVar.toString());
Run Code Online (Sandbox Code Playgroud)

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

相反,如果你有

print(nameof(myVar) + " value is " + myVar.toString());
Run Code Online (Sandbox Code Playgroud)

这将有助于自动重构!