目录
二. CallerMemberName简化InotifyPropertyChange的实现
一.CallerMemberName属性介绍
在开发过程中,我们有时候需要记录一下调用信息,比如有下面一个函数:
public void DoSomething()
{
TraceMessage("事情开始起变化!");
}
为测试调试方便,除了事件信息外,我们还想知道该事件的代码位置,以及调用信息。在C++中,我们可以定义一个宏,在宏中通过__FILE__和__LINE__来获取当前代码的位置,但C#不支持宏,因此要实现类似的功能比较麻烦。针对这个问题,在.Net 4.5中引入了三个Attribute:CallerMemberName、CallerFilePath和CallerLineNumber。在编译器的配合下,分别可以获取到调用函数(准确讲应该是成员)名称,调用文件及调用行号。上面的TraceMessage函数可以实现如下:
public static void TraceMessage(string message,
[CallerMemberName] string memberName="",
[CallerFilePath] string sourceFilePath="",
[CallerLineNumber] int sourceLineNumber=0)
{
Console.WriteLine($"message:{message}\nmember name: {memberName}" +
$"\nsource file path: {sourceFilePath}\nsource line number: {sourceLineNumber}");
}
调用的结果如下:
message:事情开始起变化!
member name: DoSomething
source file path: /Users/qinyuanlong/DotNet_core_x1/CallerMemberNameConsole/CallerMemberNameConsole/Program.cs
source line number: 16
另外,在构造函数,析构函数、属性等特殊的地方调用CallerMemberName属性所标记的函数时,获取的值有所不同,其取值如下表所示:
调用的地方 | CallerMemberName获取的结果 |
方法、属性或事件 | 方法,属性或事件的名称 |
构造函数 | 字符串 ".ctor" |
静态构造函数 | 字符串 ".cctor" |
析构函数 | 该字符串 "Finalize" |
用户定义的运算符或转换 | 生成的名称成员,例如, "op_Addition"。 |
特性构造函数 | 特性所应用的成员的名称 |
二. CallerMemberName简化InotifyPropertyChange的实现
在WPF中,当我们要使用MVVM的方式绑定一个普通对象的属性时,界面上往往需要获取到属性变更的通知。一般我们会新建一个类,并继承InotifyPropertyChange接口。
class NotifyObject : INotifyPropertyChanged
{
private int number;
public int Number
{
get { return number; }
set { number = value; OnPropertyChanged("Number"); }
}
private string text;
public string Text
{
get { return text; }
set { text = value; OnPropertyChanged("Text"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这么做有一个比较大的隐患,那就是用了字符串的硬编码的方式传递了属性名称,一旦拼写错误或因为重构代码忘记去更新这个字符串时,这样就会导致界面上得不到更新。(本身硬编码的方式来保证两者的一致性就是不靠谱的行为)
刚好上面的属性可以用来实现。
class NotifyObject : INotifyPropertyChanged
{
private int number;
public int Number
{
get { return number; }
set { number = value; OnPropertyChanged(); }
}
private string text;
public string Text
{
get { return text; }
set { text = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
在新的OnpertyChangeEventHandler,用[CallerMemberName]属性修饰参数,那么在某个属性发生改变时,会调用此函数,propertyName就有了该属性的名字,因此实现前面相同的功能,但我们不需要显示传入属性名了。
在属性多的时候代码就显得很累赘了。这里写了一个通用点的函数把他们统一起来,下次就可以直接用了。由于C#的语法限制,不能在类外部调用event,因此不能写成扩展方法,这里就简单的写成一个对象,下次就直接照着改好了:
class NotifyObject : INotifyPropertyChanged
{
private int number;
public int Number
{
get { return number; }
set { UpdateProper(ref number, value); }
}
private string text;
public string Text
{
get { return text; }
set { UpdateProper(ref text, value); }
}
protected void UpdateProper<T>(ref T properValue, T newValue, [CallerMemberName] string properName = "")
{
if (object.Equals(properValue, newValue))
return;
properValue = newValue;
OnPropertyChanged(properName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
本文参考博客:C#5.0 新特性学习之CallerMemberName,使用CallerMemberName简化InotifyPropertyChanged的实现