0. 目录
C#6 新增特性目录
1. 老版本的代码
1 using System;
2 namespace csharp6
3 {
4 internal class Program
5 {
6 private static void Main(string[] args)
7 {
8 if (args==null)
9 {
10 throw new ArgumentNullException("args");
11 }
12 }
13 }
14 }
这段代码并没什么问题,运行良好。随着时间的推移,有一天,我觉得args这个参数名不合适,想改一个更直观的名字filePaths,表示我要接受一个文件路径的数组。然后我们就直接把args这个名字给重构了,but,把 throw new ArgumentNullException("args"); 给忘了(resharper重构可能会同时重构这个名字),因为它仅仅是个字符串,书写的时候容易拼错,重构的时候也无法对它进行一个是否需要重构的分析,导致一些麻烦事情。
那么nameof运算符的目的就是来解决这个问题的。
2. nameof 运算符
nameof是C#6新增的一个关键字运算符,主要作用是方便获取类型、成员和变量的简单字符串名称(非完全限定名),意义在于避免我们在代码中写下固定的一些字符串,这些固定的字符串在后续维护代码时是一个很繁琐的事情。比如上面的代码改写后:
1 using System;
2 namespace csharp6
3 {
4 internal class Program
5 {
6 private static void Main(string[] args)
7 {
8 if (args==null)
9 {
10 throw new ArgumentNullException(nameof(args));
11 }
12 }
13 }
14 }
我们把固定的 "args" 替换成等价的 nameof(args) 。按照惯例,贴出来两种方式的代码的IL。
"args"方式的IL代码:
1 .method private hidebysig static void Main(string[] args) cil managed
2 {
3 .entrypoint
4 // Code size 22 (0x16)
5 .maxstack 2
6 .locals init ([0] bool V_0)
7 IL_0000: nop
8 IL_0001: ldarg.0
9 IL_0002: ldnull
10 IL_0003: ceq
11 IL_0005: stloc.0
12 IL_0006: ldloc.0
13 IL_0007: brfalse.s IL_0015
14 IL_0009: nop
15 IL_000a: ldstr "args"
16 IL_000f: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string)
17 IL_0014: throw
18 IL_0015: ret
19 } // end of method Program::Main
nameof(args)方式的IL代码:
1 .method private hidebysig static void Main(string[] args) cil managed
2 {
3 .entrypoint
4 // Code size 22 (0x16)
5 .maxstack 2
6 .locals init ([0] bool V_0)
7 IL_0000: nop
8 IL_0001: ldarg.0
9 IL_0002: ldnull
10 IL_0003: ceq
11 IL_0005: stloc.0
12 IL_0006: ldloc.0
13 IL_0007: brfalse.s IL_0015
14 IL_0009: nop
15 IL_000a: ldstr "args"
16 IL_000f: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string)
17 IL_0014: throw
18 IL_0015: ret
19 } // end of method Program::Main
一样一样的,我是没看出来有任何的差异,,,so,这个运算符也是一个编译器层面提供的语法糖,编译后就没有nameof的影子了。
3. nameof 注意事项
nameof可以用于获取具名表达式的当前名字的简单字符串表示(非完全限定名)。注意当前名字这个限定,比如下面这个例子,你觉得会输出什么结果?
1 using static System.Console;
2 using CC = System.ConsoleColor;
3
4 namespace csharp6
5 {
6 internal class Program
7 {
8 private static void Main()
9 {
10 WriteLine(nameof(CC));//CC
11 WriteLine(nameof(System.ConsoleColor));//ConsoleColor
12 }
13 }
14 }
第一个语句输出"CC",因为它是当前的名字,虽然是指向System.ConsoleColor枚举的别名,但是由于CC是当前的名字,那么nameof运算符的结果就是"CC"。
第二个语句输出了"ConsoleColor",因为它是System.ConsoleColor的简单字符串表示,而非取得它的完全限定名,如果想取得"System.ConsoleColor",那么请使用 typeof(System.ConsoleColor).FullName 。再比如微软给的例子: nameof(person.Address.ZipCode) ,结果是"ZipCode"。
4. 参考
C#语言参考-运算符:nameof
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
最重要的是nameof不会影响性能!
nameof有什么用?主要用解决 类成员名做参数替代成员们的字符串做参数,如下:
using System;
namespace csharp6
{
internal class Program
{
private static void Main(string[] args)
{
if (args==null)
{
throw new ArgumentNullException("args");//旧的写法 变量名的字符串做参数
//throw new ArgumentNullException(nameOf(args));//新的写法 避免了args变量名更改后,忘记更改字符串"args",因为字符串编译器是不错提示错误的
} } } }
这样非常有利于后期项目维护,比如我们在使用MVC开发时候,后端返回到某个视图,我们平时喜欢写字符串的形式,如果项目越来越大,后期突然哪个控制器或者动作不用了,使用字符串的形式维护起来就非常麻烦,用nameof就可以很好的解决,最重要的是不会影响性能!
nameof 运算符
nameof是C#6新增的一个关键字运算符,主要作用是方便获取类型、成员和变量的简单字符串名称(非完全限定名),意义在于避免我们在代码中写下固定的一些字符串,这些固定的字符串在后续维护代码时是一个很繁琐的事情。比如上面的代码改写后:
using System;
namespace csharp6
{
internal class Program
{
private static void Main(string[] args)
{
if (args==null)
{
throw new ArgumentNullException(nameof(args));
}
}
}
}
我们把固定的 "args" 替换成等价的 nameof(args) 。按照惯例,贴出来两种方式的代码的IL。
"args"方式的IL代码
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 22 (0x16)
.maxstack 2
.locals init ([0] bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldnull
IL_0003: ceq
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: brfalse.s IL_0015
IL_0009: nop
IL_000a: ldstr "args"
IL_000f: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string)
IL_0014: throw
IL_0015: ret
} // end of method Program::Main
nameof(args)方式的IL代码:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 22 (0x16)
.maxstack 2
.locals init ([0] bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldnull
IL_0003: ceq
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: brfalse.s IL_0015
IL_0009: nop
IL_000a: ldstr "args"
IL_000f: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string)
IL_0014: throw
IL_0015: ret
} // end of method Program::Main
一样一样的,我是没看出来有任何的差异,,,so,这个运算符也是一个编译器层面提供的语法糖,编译后就没有nameof的影子了。
3. nameof 注意事项
nameof可以用于获取具名表达式的当前名字的简单字符串表示(非完全限定名)。注意当前名字这个限定,比如下面这个例子,你觉得会输出什么结果?
using static System.Console;
using CC = System.ConsoleColor;
namespace csharp6
{
internal class Program
{
private static void Main()
{
WriteLine(nameof(CC));//CC
WriteLine(nameof(System.ConsoleColor));//ConsoleColor
}
}
}
第一个语句输出"CC",因为它是当前的名字,虽然是指向System.ConsoleColor枚举的别名,但是由于CC是当前的名字,那么nameof运算符的结果就是"CC"。
第二个语句输出了"ConsoleColor",因为它是System.ConsoleColor的简单字符串表示,而非取得它的完全限定名,如果想取得"System.ConsoleColor",那么请使用 typeof(System.ConsoleColor).FullName 。再比如微软给的例子: nameof(person.Address.ZipCode) ,结果是"ZipCode"。
以上内容来自:
https://www.cnblogs.com/lsgsanxiao/p/10977335.html
以前我们使用的是这样的:
| |
| SetFieldReadOnly( () => Entity.UserName ); |
| ... |
| |
| private void SetFieldReadOnly(Expression<Func<object>> property) |
| { |
| var propName = GetPropNameFromExpr(property); |
| SetFieldsReadOnly(propName); |
| } |
|
|
| private void SetFieldReadOnly(string propertyName) |
| { |
| ... |
| } |
原因-编译时间安全。 没有人可以默默地重命名属性并破坏代码逻辑。 现在我们可以使用nameof()了。
如果要重用属性名称,例如在基于属性名称引发异常或处理 PropertyChanged
事件时,该怎么办? 在很多情况下,您都希望使用属性名称。
举个例子:
| switch (e.PropertyName) |
| { |
| case nameof(SomeProperty): |
| { break; } |
|
|
| |
| case "SomeOtherProperty": |
| { break; } |
| } |
在第一种情况下,重命名 SomeProperty
也将更改属性的名称,否则将中断编译。 最后一种情况没有。
这是保持代码编译和消除错误(排序)的一种非常有用的方法。
( 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
和日志记录-在两种情况下,您都希望将要调用的成员的名称传递给另一个方法:
| |
| public int Foo |
| { |
| get { return this.foo; } |
| set |
| { |
| this.foo = value; |
| PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.Foo)); |
| } |
| } |
要么...
| |
| void DoSomething(... params ...) |
| { |
| Log(nameof(DoSomething), "Message...."); |
| } |
我能想到的最常见的用例是使用 INotifyPropertyChanged
接口时。 (基本上,与WPF和绑定有关的所有内容都使用此接口)
看一下这个例子:
| public class Model : INotifyPropertyChanged |
| { |
| |
| public event PropertyChangedEventHandler PropertyChanged; |
|
|
| private string foo; |
| public String Foo |
| { |
| get { return this.foo; } |
| set |
| { |
| this.foo = value; |
| |
| PropertyChanged(this, new PropertyChangedEventArgs("Foo")); |
|
|
| |
| PropertyChanged(this, new PropertyChangedEventArgs(nameof(Foo))); |
| } |
| } |
| } |
如您所见,我们必须传递一个字符串以指示哪个属性已更改。 使用 nameof
我们可以直接使用属性的名称。 这似乎没什么大不了的。 但是想像一下当有人更改属性 Foo
的名称时会发生什么。 使用字符串时,绑定将停止工作,但编译器不会警告您。 使用nameof时,会出现一个编译器错误,即没有名称为 Foo
属性/参数。
请注意,某些框架使用一些反射魔术来获取属性的名称,但是现在我们有了nameof,它不再是必需的 。
最常见的用法是在输入验证中,例如
| |
| void Foo(string par) { |
| if (par == null) throw new ArgumentNullException("par"); |
| } |
|
|
| |
| void Foo(string par) { |
| if (par == null) throw new ArgumentNullException(nameof(par)); |
| } |
在第一种情况下,如果重构更改 par 参数名称的方法,则可能会忘记在 ArgumentNullException中 进行更改。 使用 nameof, 您不必担心。
另请参见: nameof(C#和Visual Basic参考)
考虑到您在代码中使用了变量,并且需要获取变量的名称并可以说将其打印出来,因此您必须使用
| int myVar = 10; |
| print("myVar" + " value is " + myVar.toString()); |
然后如果有人重构代码并为“ myVar”使用另一个名称,则他/她将必须注意代码中的字符串值并相应地对其进行处理。
相反,如果您有
| print(nameof(myVar) + " value is " + myVar.toString()); |
这将有助于自动重构!
nameof
关键字的用法之一是用于以 编程方式 在wpf中设置 Binding
。
要设置 Binding
您必须使用字符串和 nameof
关键字设置 Path
,可以使用Refactor选项。
例如,如果您在 UserControl
具有 IsEnable
依赖项属性,并且要将其绑定到 UserControl
中某些 CheckBox
IsEnable
,则可以使用以下两个代码:
| 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); |
很明显,第一个代码无法重构,而第二个代码可以重构。
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)); |
要么:
| 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))] |
事实是,在这种情况下,我什至不需要生成的属性来访问资源,但是现在有了编译时检查资源是否存在。
C#6.0的 nameof
功能变得很方便的另一个用例 -考虑像 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 }); |
|
|
| 以上内容来自: https: |