本筆記摘抄自:https://www.cnblogs.com/susufufu/p/6882498.html,記錄一下學習過程以備後續查用。 一、官方概述 特性提供功能強大的方法,用以將元數據或聲明信息與代碼(程式集、類型、方法、屬性等)相關聯。 特性與程式實體關聯後,即可在運行時使用名 為“反射” ...
本筆記摘抄自: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特性。
四、關聯特性
利用反射的原理,關聯特性類與目標類型(反射:主要利用Type類的屬性和方法來獲得一個目標類型的類型信息對象,然後根據該對象可以得到目標
類型的信息,如它的欄位、屬性、方法名、類名等,有了這些信息,下一步就可以為所欲為了,可以還原該類型,即反系列化,甚至創建一個新類型)。
示例代碼如下:
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(); } } } }View Code
運行結果如下:
當編譯器發現一個特性應用到一個目標併發生關聯時:
1)首先會把"Attribute"追加到特性的名稱(若使用了簡寫),形成完整的特性類名。
2)然後在其所有引入的命名空間中搜索該特性類,若找不到該類或它與目標不匹配時,則產生編譯錯誤。
3)檢查傳遞給特性的參數,並查找該特性中帶定位參數的構造函數(或無參構造函數)和其它可選的命名參數(特性類的公共欄位、屬性),若找到匹配
的構造函數,則實例化該特性類,編譯器還會把目標類型的元數據傳遞給程式集,反射可以從程式集中讀取元數據,若找不到則產生編譯錯誤。
關聯代碼也可以定義在目標類型的內部:
[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內置程式集的某個地方判斷該目標類型是否應用了該特性,然後決定是否進行系列化操作。