C# CallerMemberName特性介紹以及簡化InotifyPropertyChanged的實現

目錄

 

一.CallerMemberName屬性介紹

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章