6.0版具有的新功能nameof
,但我无法理解它的用途,因为它只接受变量名,并在编译时将其更改为字符串。
我认为使用<T>
时它可能有一些用途,但是当我尝试使用它时nameof(T)
,只会打印出a T
而不是使用的类型。
有什么目的吗?
T
。以前有一种获取二手类型的方法。
nameof
。还有助于防止错别字。
6.0版具有的新功能nameof
,但我无法理解它的用途,因为它只接受变量名,并在编译时将其更改为字符串。
我认为使用<T>
时它可能有一些用途,但是当我尝试使用它时nameof(T)
,只会打印出a T
而不是使用的类型。
有什么目的吗?
T
。以前有一种获取二手类型的方法。
nameof
。还有助于防止错别字。
Answers:
如果要重用属性名称,例如在基于属性名称引发异常或处理PropertyChanged
事件时,该怎么办?在很多情况下,您都希望使用属性名称。
举个例子:
switch (e.PropertyName)
{
case nameof(SomeProperty):
{ break; }
// opposed to
case "SomeOtherProperty":
{ break; }
}
在第一种情况下,重命名SomeProperty
也将更改属性的名称,否则将中断编译。最后一种情况没有。
这是保持代码编译和消除错误(排序)的非常有用的方法。
(埃里克·利珀特(Eric Lippert)的一篇非常好的文章,为什么infoof
没有做到,而同时nameof
却做到了)
nameof
和动作名称而不是硬编码字符串进行路由。
public class MyController { public ActionResult Index() { return View(nameof(Index)); } }
,您可以nameof
在非静态成员上使用(例如,可以nameof(MyController.Index)
使用上面的类进行调用,它将发出“索引”)。在msdn.microsoft.com/en-us/library/…上
它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)
,这是另一段在类似情况下有用的编译时糖:-)
nameofthismethod
。您可以使用,Log.Error($"Error in {nameof(DoSomething)}...")
但是如果将其复制粘贴到其他方法中,您不会注意到它仍在引用DoSomething
。因此,当它与局部变量或参数完美配合时,方法名是一个问题。
nameOf
会使用该[DisplayName]
属性(如果存在)?例如,enum
我[DisplayName]
经常在MVC项目中使用
throw new
是另一种反模式-我发现过度使用catch
是初级开发人员的常见问题,因为感觉就像是在解决问题(大多数时候只是将其隐藏)。
nameof
C#6.0的功能变得很方便的另一个用例 -考虑像Dapper这样的库,它使DB检索更加容易。尽管这是一个很棒的库,但是您需要在查询中对属性/字段名称进行硬编码。这意味着如果您决定重命名属性/字段,则很有可能会忘记更新查询以使用新的字段名。使用字符串插值和nameof
功能,代码变得更易于维护和类型安全。
从链接中给出的示例
没有名字
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });
与nameof
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
)中创建的WPF绑定或调用反射来查找特定对象如何?成员或类型?
我能想到的最常见的用例是使用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,它不再是必需的。
[CallerMemberName]
在新方法的参数上使用该属性来引发此事件。
[CallerMemberName]string x = null
比更好nameof(Property)
。您可以说属性名使用了两次,但这基本上就是传递给函数的参数。我认为DRY实际上不是什么意思:)。
nameof
是,属性设置程序根本不需要指定属性名称,从而消除了复制/粘贴错误的可能性。
INotifyPropertyChanged
,这是一种“更好的在一起”的情况,使用[CallerMemberNameAttribute]
允许从属性设置程序中清晰地发出更改通知,而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 MVC的核心项目使用nameof
的AccountController.cs
,并ManageController.cs
与RedirectToAction
方法在控制器中引用的动作。
例:
return RedirectToAction(nameof(HomeController.Index), "Home");
转换为:
return RedirectToAction("Index", "Home");
并将用户带到“主页”控制器(即)中的“索引”操作/Home/Index
。
return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Substring(nameof(HomeController),0,nameof(HomeController).Length-"Controller".Length));
呢?
正如其他人已经指出的那样,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
运算符。
考虑到您在代码中使用了变量,并且需要获取变量的名称并可以说将其打印出来,因此您必须使用
int myVar = 10;
print("myVar" + " value is " + myVar.toString());
然后如果有人重构代码并为“ myVar”使用另一个名称,则他/她将必须注意代码中的字符串值并相应地对其进行处理。
相反,如果您有
print(nameof(myVar) + " value is " + myVar.toString());
这将有助于自动重构!
Type
,和值。这将使代码调用日志记录方法消除大量冗余成为可能。
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
关键字的用法之一是用于Binding
以编程方式在wpf中进行设置。
要设置Binding
您必须Path
使用字符串和nameof
关键字进行设置,可以使用“重构”选项。
举例来说,如果你有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);
很明显,第一个代码无法重构,而第二个代码可以重构。
以前我们使用的是这样的:
// 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()了。
使用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
增加了我的应用程序中非常长且复杂的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 },
}
}