C#特性學習筆記

chinaherolts2008發表於2020-12-21
本筆記摘抄自:https://www.cnblogs.com/susufufu/p/6882498.html,記錄一下學習過程以備後續查用。

一、官方概述

特性提供功能強大的方法,用以將後設資料或宣告資訊與程式碼(程式集、型別、方法、屬性等)相關聯。 特性與程式實體關聯後,即可在執行時使用名

為“反射”的技術查詢特性。

特性,如Serializable,它其實就是一個類,定義方式跟類一樣,且所有特性都是直接或間接繼承自Attribute基類。

二、自定義一個特性

自定義一個特性PermissionAttribute:
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
        public class PermissionAttribute : Attribute    //類名是特性的名稱
        {
            public string compNo;                       //命名引數
            private string _popeId, _popeName;
            public PermissionAttribute(string popeId,string popeName)   //popeId、popeName為定位引數
            {
                compNo = "DB_TEST";
                _popeId = popeId;
                _popeName = popeName;
            }
        }

下面讓我們看看,如何建立一個自定義特性?

1)一個自定義特性類必須直接或間接繼承自System.Attribute特性類。

2)為該自定義特性類指定System.AttributeUsage特性,並指定限制引數(列舉System.AttributeTargets和可選的AllowMultiple、Inherited命名引數)。

AttributeUsage的命名引數:AllowMultiple表示是否允許多次使用在同一目標上、Inherited表示是否同時應用於派生型別或過載版本。

3)類名是特性的名稱。

4)建構函式的引數是自定義特性的定位引數(應用該特性時必須放在引數列表的最前面),也可以是無參建構函式(如[Serializable])。

5)任何公共的讀寫欄位或屬性都是可選的命名引數。

6)如果特性類包含一個屬性,則該屬性必須為讀寫屬性。

三、應用特性

示例程式碼如下:
        [Permission("01_01","使用者資料",compNo ="DB_SYSTEM")]
        public class Users
        {
            public Users()
            {

            }
            public int AddUser(string userId,string userName)
            {
                return 1;
            }
        }
應用特性 [Permission("01_01","使用者資料",compNo ="DB_SYSTEM")] 其實是通過建構函式的呼叫來例項化一個特性類。

根據約定,所有特性名稱都以單詞“Attribute”結束,如可系列化標記特性Serializable,它的全稱為SerializableAttribute。在程式碼中使用特性時,不需要

指定Attribute字尾,如上面程式碼,只需用Permission即可代表PermissionAttribute特性。

四、關聯特性

利用反射的原理,關聯特性類與[vb.net教程](https://www.xin3721.com/eschool/VBNetxin3721/)

目標型別(反射:主要利用Type類的屬性和方法來獲得一個目標型別的型別資訊物件,然後根據該物件可以得到目標

型別的資訊,如它的欄位、屬性、方法名、類名java基礎
等,有了這些資訊,下一步就可以為所欲為了,可以還原該型別,即反系列化,甚至建立一個新型別)。

示例程式碼如下:
    class Program
    {
        [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
        public class PermissionAttribute : Attribute    //類名是特性的名稱
        {
            public string compNo;                       //命名引數
            private string _popeId, _popeName;
            public PermissionAttribute(string popeId,string popeName)   //popeId、popeName為定位引數
            {
                compNo = "DB_TEST";
                _popeId = popeId;
                _popeName = popeName;
            }
        }

        [Permission("01_01","使用者資料",compNo ="DB_SYSTEM")]
        public class Users
        {
            public Users()
            {

            }
            public int AddUser(string userId,string userName)
            {
                return 1;
            }
        }

        static void Main(string[] args)
        {
            //1、判斷Users類定義時,是否應用了該特性?
            if (typeof(Users).IsDefined(typeof(PermissionAttribute),false))
            {
                //2、獲得該特性物件,之後就可以訪問它的成員(後設資料)。
                PermissionAttribute attribute = (PermissionAttribute)Attribute.GetCustomAttribute(typeof(Users), typeof(PermissionAttribute));
                if(attribute.compNo == "DB_SYSTEM")
                {
                    Console.WriteLine("Hello World.");
                    Console.Read();
                }
            }
        }
    }

執行結果如下:



當編譯器發現一個特性應用到一個目標併發生關聯時:

1)首先會把"Attribute"追加到[python基礎教程](https://www.xin3721.com/eschool/pythonxin3721/)

特性的名稱(若使用了簡寫),形成完整的特性類名。

2)然後在其所有引入的名稱空間中搜尋該特性類,若找不到該類或它與目標不匹配時,則產生編譯錯誤。

3)檢查傳遞給特性的引數,並查詢該特性中帶定位引數的建構函式(或無參建構函式)和其它可選的命名引數(特性類[c#教程](https://www.xin3721.com/eschool/CSharpxin3721/)的公共欄位、屬性),若找到匹配

的建構函式,則例項化該特性類,編譯器還會把目標型別的後設資料傳遞給程式集,反射可以從程式集中讀取後設資料,若找不到則產生編譯錯誤。

關聯程式碼也可以定義在目標型別的內部:
    [Permission("01_01","使用者資料",compNo ="DB_SYSTEM")]
    public class Users
    {
        public Users()
        {

        }
        public int AddUser(string userId,string userName)
        {
            //1、判斷Users類定義時,是否應用了該特性?
            if (typeof(Users).IsDefined(typeof(PermissionAttribute), false))
            {
                //2、獲得該特性物件,之後就可以訪問它的成員(後設資料)。
                PermissionAttribute attribute = (PermissionAttribute)Attribute.GetCustomAttribute(typeof(Users), typeof(PermissionAttribute));
                if (attribute.compNo == "DB_SYSTEM")
                {
                    return 1;
                }
            }
            return 0;
        }
    }
五、.NET預定義特性

至於.NET預定義特性的實現原理,我沒研究過,大概類似自定義特性吧,就比如系列化特性SerializableAttribute,實現原理我想大概是這樣:應用

[Serializable]時給目標做一個“標記”,在.NET內建程式集的某個地方判斷該目標型別是否應用了該特性,然後決定是否進行系列化操作。

相關文章