目錄
二. 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的實現