名称的目的是什么?
What is the purpose of nameof?
Version 6.0 有了 nameof
的新功能,但我不明白它的用途,因为它只是获取变量名并在编译时将其更改为字符串。
我认为它在使用 <T>
时可能有一些用途,但当我尝试 nameof(T)
时,它只会打印 T
而不是使用的类型。
对目的有什么想法吗?
它对 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
当您考虑各种绑定和反射情况时,用途是多种多样的。这是将 运行 时间错误引入编译时的绝佳方式。
如果您想重用 属性 的名称,例如当基于 属性 名称抛出异常或处理 PropertyChanged
事件时,情况如何。在许多情况下,您希望获得 属性.
的名称
举个例子:
switch (e.PropertyName)
{
case nameof(SomeProperty):
{ break; }
// opposed to
case "SomeOtherProperty":
{ break; }
}
在第一种情况下,如果不更改 属性 定义和 nameof(SomeProperty)
表达式,重命名 SomeProperty
将导致编译错误。在第二种情况下,重命名 SomeOtherProperty
或更改 "SomeOtherProperty"
字符串将导致运行时行为悄无声息地中断,在构建时不会出现错误或警告。
这是保持代码编译和无错误(某种程度上)的非常有用的方法。
(A very nice article from Eric Lippert 为什么 infoof
没有成功,而 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 你就不用担心了。
假设您需要在代码中打印变量的名称。如果你写:
int myVar = 10;
print("myVar" + " value is " + myVar.toString());
然后如果有人重构代码并为 myVar
使用另一个名称,he/she 将不得不在您的代码中查找字符串值并相应地更改它。
相反,如果您这样写:
print(nameof(myVar) + " value is " + myVar.toString());
自动重构会很有帮助!
我能想到的最常见的用例是在使用 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 时会出现编译器错误,即没有 property/argument 名称 Foo
。
请注意,某些框架使用一些反射魔法来获取 属性 的名称,但现在我们有了 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))]
事实是,在这种情况下,我什至不需要生成的属性来访问资源,但现在我有编译时检查资源是否存在。
正如其他人已经指出的那样,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
运算符。
The MSDN article 列出了 MVC 路由(对我来说真正点击了这个概念的例子)等等。 (格式化的)描述段落如下:
- When reporting errors in code,
- hooking up model-view-controller (MVC) links,
- firing property changed events, etc.,
you often want to
capture the string name of a method. Using nameof helps keep your code
valid when renaming definitions.
Before you had to use string literals
to refer to definitions, which is brittle when renaming code elements
because tools do not know to check these string literals.
被接受/评分最高的答案已经给出了几个很好的具体例子。
ASP.NET 核心 MVC 项目使用 AccountController.cs
and ManageController.cs
中的 nameof
和 RedirectToAction
方法来引用控制器中的操作。
示例:
return RedirectToAction(nameof(HomeController.Index), "Home");
这转化为:
return RedirectToAction("Index", "Home");
and takes 将用户带到 'Home' 控制器中的 'Index' 操作,即 /Home/Index
.
nameof
关键字的用法之一是在 wpf 中以编程方式设置 Binding
。
要设置 Binding
,您必须使用字符串设置 Path
,并且使用 nameof
关键字,可以使用 Refactor 选项。
例如,如果您的 UserControl
中有 IsEnable
依赖项 属性,并且您想将其绑定到您的 CheckBox
中的某些 IsEnable
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);
很明显第一个代码不能重构但是第二个...
之前我们使用的是类似的东西:
// 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().
C# 6.0 的 nameof
功能变得方便的另一个用例 - 考虑像 Dapper 这样的库,它使数据库检索更加容易。尽管这是一个很棒的库,但您需要在查询中硬编码 property/field 个名称。这意味着如果您决定重命名 property/field,您很可能会忘记更新查询以使用新的字段名称。通过字符串插值和 nameof
功能,代码变得更易于维护和类型安全。
来自link
中给出的例子
没有 nameof
var dog = connection.Query<Dog>(
"select Age = @Age, Id = @Id",
new {Age = (int?) null, Id = guid});
with nameof
var dog = connection.Query<Dog>(
$"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id",
new {Age = (int?) null, Id = guid});
当您使用 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)}" })
}
}
如果您使用重构工具重命名 属性,您的验证不会被破坏。
nameof
的另一个用例是检查标签页,而不是检查索引,您可以检查标签页的 Name
属性,如下所示:
if(tabControl.SelectedTab.Name == nameof(tabSettings))
{
// Do something
}
不那么乱了:)
我发现 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的目的是重构。例如,当您更改代码中通过 nameof 引用的 class 的名称时,您将收到编译错误 ,这正是您想要的 。如果您没有使用 nameof 并且只有一个纯字符串作为参考,您必须全文搜索 class 的名称才能更改它。那是心底的痛。使用 nameof,您可以高枕无忧,在 IDE.
中自动构建和获取所有更改案例
Version 6.0 有了 nameof
的新功能,但我不明白它的用途,因为它只是获取变量名并在编译时将其更改为字符串。
我认为它在使用 <T>
时可能有一些用途,但当我尝试 nameof(T)
时,它只会打印 T
而不是使用的类型。
对目的有什么想法吗?
它对 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
当您考虑各种绑定和反射情况时,用途是多种多样的。这是将 运行 时间错误引入编译时的绝佳方式。
如果您想重用 属性 的名称,例如当基于 属性 名称抛出异常或处理 PropertyChanged
事件时,情况如何。在许多情况下,您希望获得 属性.
举个例子:
switch (e.PropertyName)
{
case nameof(SomeProperty):
{ break; }
// opposed to
case "SomeOtherProperty":
{ break; }
}
在第一种情况下,如果不更改 属性 定义和 nameof(SomeProperty)
表达式,重命名 SomeProperty
将导致编译错误。在第二种情况下,重命名 SomeOtherProperty
或更改 "SomeOtherProperty"
字符串将导致运行时行为悄无声息地中断,在构建时不会出现错误或警告。
这是保持代码编译和无错误(某种程度上)的非常有用的方法。
(A very nice article from Eric Lippert 为什么 infoof
没有成功,而 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 你就不用担心了。
假设您需要在代码中打印变量的名称。如果你写:
int myVar = 10;
print("myVar" + " value is " + myVar.toString());
然后如果有人重构代码并为 myVar
使用另一个名称,he/she 将不得不在您的代码中查找字符串值并相应地更改它。
相反,如果您这样写:
print(nameof(myVar) + " value is " + myVar.toString());
自动重构会很有帮助!
我能想到的最常见的用例是在使用 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 时会出现编译器错误,即没有 property/argument 名称 Foo
。
请注意,某些框架使用一些反射魔法来获取 属性 的名称,但现在我们有了 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))]
事实是,在这种情况下,我什至不需要生成的属性来访问资源,但现在我有编译时检查资源是否存在。
正如其他人已经指出的那样,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
运算符。
The MSDN article 列出了 MVC 路由(对我来说真正点击了这个概念的例子)等等。 (格式化的)描述段落如下:
- When reporting errors in code,
- hooking up model-view-controller (MVC) links,
- firing property changed events, etc.,
you often want to capture the string name of a method. Using nameof helps keep your code valid when renaming definitions.
Before you had to use string literals to refer to definitions, which is brittle when renaming code elements because tools do not know to check these string literals.
被接受/评分最高的答案已经给出了几个很好的具体例子。
ASP.NET 核心 MVC 项目使用 AccountController.cs
and ManageController.cs
中的 nameof
和 RedirectToAction
方法来引用控制器中的操作。
示例:
return RedirectToAction(nameof(HomeController.Index), "Home");
这转化为:
return RedirectToAction("Index", "Home");
and takes 将用户带到 'Home' 控制器中的 'Index' 操作,即 /Home/Index
.
nameof
关键字的用法之一是在 wpf 中以编程方式设置 Binding
。
要设置 Binding
,您必须使用字符串设置 Path
,并且使用 nameof
关键字,可以使用 Refactor 选项。
例如,如果您的 UserControl
中有 IsEnable
依赖项 属性,并且您想将其绑定到您的 CheckBox
中的某些 IsEnable
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);
很明显第一个代码不能重构但是第二个...
之前我们使用的是类似的东西:
// 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().
C# 6.0 的 nameof
功能变得方便的另一个用例 - 考虑像 Dapper 这样的库,它使数据库检索更加容易。尽管这是一个很棒的库,但您需要在查询中硬编码 property/field 个名称。这意味着如果您决定重命名 property/field,您很可能会忘记更新查询以使用新的字段名称。通过字符串插值和 nameof
功能,代码变得更易于维护和类型安全。
来自link
中给出的例子没有 nameof
var dog = connection.Query<Dog>(
"select Age = @Age, Id = @Id",
new {Age = (int?) null, Id = guid});
with nameof
var dog = connection.Query<Dog>(
$"select {nameof(Dog.Age)} = @Age, {nameof(Dog.Id)} = @Id",
new {Age = (int?) null, Id = guid});
当您使用 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)}" })
}
}
如果您使用重构工具重命名 属性,您的验证不会被破坏。
nameof
的另一个用例是检查标签页,而不是检查索引,您可以检查标签页的 Name
属性,如下所示:
if(tabControl.SelectedTab.Name == nameof(tabSettings))
{
// Do something
}
不那么乱了:)
我发现 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的目的是重构。例如,当您更改代码中通过 nameof 引用的 class 的名称时,您将收到编译错误 ,这正是您想要的 。如果您没有使用 nameof 并且只有一个纯字符串作为参考,您必须全文搜索 class 的名称才能更改它。那是心底的痛。使用 nameof,您可以高枕无忧,在 IDE.
中自动构建和获取所有更改案例