版本6.0增加了一个新功能nameof
,但我不理解它的目的,因为它只是在编译时将变量名更改为字符串。
我以为在使用<T>
时可能有一些用途,但当我尝试nameof(T)
时,它只会打印出T
而不是所使用的类型。
对此有什么想法吗?
如果需要重复使用属性名称,例如基于属性名称抛出异常或处理PropertyChanged
事件等情况,该怎么办?有许多情况下你需要获取属性的名称。
以此示例为例:
switch (e.PropertyName)
{
case nameof(SomeProperty):
{ break; }
// opposed to
case "SomeOtherProperty":
{ break; }
}
nameof(SomeProperty)
表达式,则重命名SomeProperty
将导致编译错误。在第二种情况下,重命名SomeOtherProperty
或更改"SomeOtherProperty"
字符串将导致运行时行为默默中断,构建时没有错误或警告。infoof
未能成为标准,而nameof
成为了标准)nameof
和动作名称进行路由,而不是使用硬编码的字符串。 - RJ Cuthbertsonpublic class MyController { public ActionResult Index() { return View(nameof(Index)); } }
那样使用它 - 并且您可以在非静态成员上使用 nameof
(例如,您可以使用上面的类调用 nameof(MyController.Index)
,它将发出 "Index")。 请查看 https://msdn.microsoft.com/zh-cn/library/dn986596.aspx?f=255&MSPPError=-2147217396 上的示例。 - RJ Cuthbertson对于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....");
}
typeof(T)
,这是另一种在类似情况下非常有用的编译时语法糖 :-) - Keithnameofthismethod
的东西。你可以使用 Log.Error($"Error in {nameof(DoSomething)}...")
,但如果你将其复制粘贴到其他方法中,你就不会注意到它仍然在引用 DoSomething
。因此,虽然它在使用局部变量或参数时完美运行,但方法名是一个问题。 - Tim SchmelternameOf
存在,是否会使用[DisplayName]
属性?对于枚举示例,我经常在MVC项目中使用[DisplayName]
。 - Luke T O'Brienthrow new
是另一种反模式 - 我发现初级开发人员过度使用catch
是一个常见问题,因为它感觉像修复问题(但大多数情况下只是隐藏了问题)。 - Keith另一个使用 C# 6.0 的 nameof
特性变得方便的案例 - 考虑像 Dapper 这样的库,它使得从数据库检索数据变得更容易。尽管这是一个很棒的库,但您需要在查询中硬编码属性/字段名称。这意味着如果您决定重命名属性/字段,则很有可能会忘记更新查询以使用新的字段名称。使用字符串插值和 nameof
特性,代码变得更容易维护且类型安全。
来自链接中给出的示例
没有 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});
你的问题已经表达了目的。你必须看到这可能对记录日志或抛出异常有用。
例如:
public void DoStuff(object input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
}
这很好。如果我更改变量的名称,代码将会崩溃,而不是返回带有错误信息的异常。
当然,用途不仅限于这种简单情况。您可以在需要编写变量或属性名称的任何位置使用 nameof
。
在考虑各种绑定和反射情况时,使用方法多种多样。这是一个将运行时错误转化为编译时错误的绝佳方式。
OnPropertyChanged
方法(直接接受属性名称而不是PropertyChangedEventArgs
),或者调用反射来查找特定成员或类型? - O. R. Mapper我能想到的最常见用例是在使用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,这已不再必要。
[CallerMemberName]
属性来触发此事件。 - Drew Noakes[CallerMemberName]string x = null
是否比nameof(Property)
更好。你可以说属性名称被使用了两次,但那基本上就是传递给函数的参数。我认为这并不是DRY的意思 :)。 - Roy T.nameof
相比的好处是,属性设置器根本不需要指定属性名称,从而消除了复制/粘贴错误的可能性。 - Drew Noakes[CallerMemberNameAttribute]
可以使属性 setter 方法干净地触发属性改变通知,而 nameof
语法可以使代码中的其他位置清晰地触发属性改变通知。因此这是一个“共同更好”的情况。 - Andrew Hanlon最常见的用法是在输入验证中,例如:
//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 ,您无需担心这个问题。AccountController.cs
和ManageController.cs
中使用nameof
与RedirectToAction
方法一起引用控制器中的操作。
示例:
return RedirectToAction(nameof(HomeController.Index), "Home");
这翻译成:
return RedirectToAction("Index", "Home");
并将用户带到“Home”控制器中的“Index”操作,即/Home/Index
。
return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));
? - Suncat2000int myVar = 10;
print("myVar" + " value is " + myVar.toString());
如果有人重构代码并使用另一个名称来代替myVar
,那么他/她将不得不在您的代码中查找字符串值并相应地进行更改。
相反,如果您写:
print(nameof(myVar) + " value is " + myVar.toString());
自动重构会对提高效率很有帮助!
- 当报告代码错误时、
- 连接模型视图控制器(MVC)链接时、
- 触发属性更改事件等时,
您通常需要捕获方法的字符串名称。使用 nameof 可以在重命名定义时保持代码有效。
以前你必须使用字符串字面量来引用定义,但是当重命名代码元素时,这种方式非常脆弱,因为工具无法检查这些字符串字面量。
已经有多个接受/最高评分答案给出了几个极好的具体示例。
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
nameof
运算符。
nameof
中重构/更改名称时,这个方法确实很有用。它还有助于防止拼写错误。 - bvj[Test, TestCaseSource(typeof(MyDataClass), "TestCases")]
,可以被替换为[Test, TestCaseSource(typeof(MyDataClass), nameof(MyDataClass.TestCases))]
。更加优雅。 - Jeremy