CallerMemberNameAttribute 範例

今天在修改一個既有程式時,用上了 System.Runtime.CompilerServices 命名空間裡的 CallerMemberNameAttribute。

這次碰到的狀況,正巧是 CallerMemberNameAttribute 適用的場合:在某個方法中,取得呼叫此方法的那個物件的方法或屬性的名稱。

舉個例子,假設我們要寫一個共用的工具類別,取名為 TracerHelper,其用途是輸出呼叫端的屬性或方法名稱。

    static class TraceHelper
    {
        public static void ShowCallerMember(object obj, string memberName)
        {
            Console.WriteLine("Caller member is: {0}.{1}.", obj.GetType().Name, memberName);
        }
    }

用戶端每次呼叫時,都必須傳入固定的字串,例如底下這個 Customer 類別:

    class Customer
    {
        private string _fullName;

        public void Test()
        {
            TraceHelper.ShowCallerMember(this, "Test");
        }

        public string FullName
        {
            get
            {
                TraceHelper.ShowCallerMember(this, "FullName");
                return _fullName;
            }
        }
    }

那麼,每當我們存取 Customer 物件的 FullName 屬性,以及呼叫 Test 方法時,執行結果會像這樣:

    Caller member is: Customer.FullName.
    Caller member is: Customer.Test.

問題是,每一次呼叫 TracerHelper.ShowCallerMember() 方法就得寫一個固定的字串,萬一將來想要修改方法或屬性名稱,很可能因為忘了一併修改傳入 ShowCallerMember 方法的參數而導致程式執行結果錯誤。此時,CallerMemberNameAttribute 便可派上用場。

CallerMemberNameAttribute 是 .NET 4.5 與 C# 5.0 之後才有的新功能。此特徵項只能套用至非必要參數(optional parameters),也就是有指定預設值的參數。

先前的 TraceHelper.ShowCallerMember 方法可以改成這樣:

    static class TraceHelper
    {
        public static void ShowCallerMember(object obj, [CallerMemberName] string memberName"") // 只改這行!
        {
            Console.WriteLine("Caller member is: {0}.{1}.", obj.GetType().Name, memberName);
        }
    }

如此一來,剛才的 Customer 類別可修改成:

    class Customer
    {
        private string _fullName;

        public void Test()
        {
            TraceHelper.ShowCallerMember(this);
        }

        public string FullName
        {
            get
            {
                TraceHelper.ShowCallerMember(this);
                return _fullName;
            }
        }
    }

現在已經沒有固定的字串了。編譯器在編譯代碼時會幫我們取出呼叫端的方法或屬性名稱,作為 TraceHelper.ShowCallerMember() 的傳入參數。

這是編譯時期發生的事,所以並不會增加執行時期的效能負擔。

除了 [CallerMemberName],另外還有兩個類似的特徵項,也是套用於方法的傳入參數:
  • [CallerFilePath]:取得呼叫端所屬的原始碼檔案的完整路徑。
  • [CallerLineNumber]:取得呼叫端的程式碼行號。

Happy coding!


沒有留言:

技術提供:Blogger.
回頂端⬆️