6.0 版有一个新特性nameof
,但我不明白它的用途,因为它只是接受变量名并在编译时将其更改为字符串。
我认为它在使用时可能有一些目的,<T>
但是当我尝试使用它时,nameof(T)
它只会打印我一个T
而不是使用的类型。
任何想法的目的?
如果您想重用属性名称,例如基于属性名称抛出异常或处理PropertyChanged
事件时,该怎么办?在很多情况下,您都希望拥有该属性的名称。
举个例子:
switch (e.PropertyName)
{
case nameof(SomeProperty):
{ break; }
// opposed to
case "SomeOtherProperty":
{ break; }
}
在第一种情况下,SomeProperty
如果您不更改属性定义和nameof(SomeProperty)
表达式,重命名将导致编译错误。在第二种情况下,重命名SomeOtherProperty
或更改"SomeOtherProperty"
字符串将导致运行时行为静默中断,在构建时不会出现错误或警告。
这是一种非常有用的方法,可以让您的代码保持编译和无错误(有点)。
(埃里克·利珀特( Eric Lippert)的一篇非常好的文章,为什么infoof
没有成功,而nameof
成功了)
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....");
}
你的问题已经表达了目的。您必须看到这对于记录或抛出异常可能很有用。
例如:
public void DoStuff(object input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
}
这很好。如果我更改变量的名称,代码将中断,而不是返回带有错误消息的异常。
当然,用途不限于这种简单的情况。您可以nameof
在对变量或属性的名称进行编码时使用。
当您考虑各种绑定和反射情况时,用途是多种多样的。这是将运行时错误带到编译时的绝佳方式。
nameof
C# 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});
我能想到的最常见的用例是使用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 这不再是必需的。
最常见的用法是输入验证,例如
//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,您不必担心这一点。
ASP.NET Core MVC 项目使用nameof
inAccountController.cs
和ManageController.cs
withRedirectToAction
方法来引用控制器中的操作。
例子:
return RedirectToAction(nameof(HomeController.Index), "Home");
这转化为:
return RedirectToAction("Index", "Home");
并将用户带到“主页”控制器中的“索引”操作,即/Home/Index
。
假设您需要在代码中打印变量的名称。如果你写:
int myVar = 10;
print("myVar" + " value is " + myVar.toString());
然后如果有人重构代码并为 使用另一个名称myVar
,他/她将不得不在您的代码中查找字符串值并相应地更改它。
相反,如果你写:
print(nameof(myVar) + " value is " + myVar.toString());
这将有助于自动重构!
MSDN 文章列出了 MVC 路由(对我来说真正点击了这个概念的示例)等等。(格式化的)描述段落内容如下:
- 在报告代码错误时,
- 连接模型-视图-控制器 (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
但是,如果您打算混淆您的软件,这可能是一个缺点。混淆后嵌入的字符串可能不再匹配元素的名称。依赖此文本的机制将会崩溃。例如,包括但不限于:Reflection、NotifyPropertyChanged ...
在运行时确定名称会消耗一些性能,但对于混淆是安全的。如果既不需要也没有计划进行混淆,我建议使用nameof
运算符。
运算符的目的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))]
事实是,在这种情况下,我什至不需要生成的属性来访问资源,但现在我有一个编译时检查资源是否存在。
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.
我发现这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 },
}
}
的另一个用例nameof
是检查标签页,而不是检查索引,您可以检查标签页的Name
属性,如下所示:
if(tabControl.SelectedTab.Name == nameof(tabSettings))
{
// Do something
}
不那么凌乱:)
以前我们使用类似的东西:
// 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()。
nameof
关键字的用途之一是以编程Binding
方式在 wpf中设置。
要设置Binding
您必须Path
使用字符串和nameof
关键字进行设置,可以使用 Refactor 选项。
例如,如果你有IsEnable
依赖属性UserControl
并且你想将它绑定到你IsEnable
的一些CheckBox
,UserControl
你可以使用这两个代码:
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);
很明显,第一个代码无法重构,但第二个代码......
当您使用 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)}" })
}
}
如果您使用重构工具重命名属性,您的验证不会被破坏。