C#的变迁史 - C# 5.0 之调用信息增强篇
Caller Information
CallerInformation是一个简单的新特性,包括三个新引入的Attribute,使用它们可以用来获取方法调用者的信息,
这三个Attribute在System.Runtime.CompilerServices命名空间下,分别叫做CallerMemberNameAttribute,CallerFilePathAttribute和CallerLineNumberAttribute。
CallerMemberNameAttribute:用来获取方法调用者的名称
CallerFilePathAttribute:用来获取方法调用者的源代码文件路径
CallerLineNumberAttribute:用来获取方法调用者所在的行号
简单看一个小例子:
using System; using System.Runtime.CompilerServices; class Program { static void Main(string[] args) { DoProcessing(); Console.ReadKey(); } static void DoProcessing() { TraceMessage("Something happened."); } static void TraceMessage(string message, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { Console.WriteLine("message: " + message); Console.WriteLine("member name: " + memberName); Console.WriteLine("source file path: " + sourceFilePath); Console.WriteLine("source line number: " + sourceLineNumber); } }
不用多说了,结果很清楚。
不过需要注意,Caller Info的这些Attribute只能用在optional parameter上,也就是说要指定默认值的。
此外对于CallerMemberNameAttribute需要多说一点,使用这个Attribute可以避免在程序中硬编码调用方的函数名,至于这个属性返回的名字一般是这样的:
调用方法的类型 | 获取的结果 |
方法,属性,事件 | 方法,属性或事件的名字 |
构造函数 | 字符串".ctor" |
静态构造函数 | 字符串".cctor" |
Finalize函数 | 字符串"Finalize" |
用户自定义的操作 | 编译产生的名字,比如"op_Addition" |
Attribute构造函数 | 应用此Attribute的成员函数名字。如果不是应用于成员的Attribute,比如说是应用于类型的Attribute,则使用其设置的默认值 |
这个特性在WPF的MVVM模式中很有用。在WPF的MVVM设计模式中的viewmodel里,我们是用INotifyPropertyChanged接口去通知在viewmodel里的改变。具体实现中很多的Binding是通过字符串来标识的,当修改一个Property的名字的时候,有时候很可能会忘记更新通知中的字符串名字,使用这个新的CallerMemberNameAttribute就能避免这个问题。
例如以前通常的一种实现是这样的:
public class EmployeeVM:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } } }
这种实现中Property的名字"Name"是以字符串的形式来约定的,一旦这个Property被Rename后忘记改了此处,就会带来bug。使用新的Attribute以后就可以这么写了:
public class EmployeeVM:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged([CallerMemberName] string propertyName=null) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged(); } } }
这么一来就不用硬编码了,简单。