1、官方概述
併發
特性提供功能強大的方法,用以將元數據或聲明信息與代碼(程序集、類型、方法、屬性等)相關聯。 特性與程序實體關聯後,便可在運行時使用名ide
爲「反射」的技術查詢特性。函數
特性,如Serializable,它其實就是一個類,定義方式跟類同樣,且全部特性都是直接或間接繼承自Attribute基類。對象
2、自定義一個特性blog
自定義一個特性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; } }
下面讓咱們看看,如何建立一個自定義特性?ip
1)一個自定義特性類必須直接或間接繼承自System.Attribute特性類。get
2)爲該自定義特性類指定System.AttributeUsage特性,並指定限制參數(枚舉System.AttributeTargets和可選的AllowMultiple、Inherited命名參數)。編譯器
AttributeUsage的命名參數:AllowMultiple表示是否容許屢次使用在同一目標上、Inherited表示是否同時應用於派生類型或重載版本。string
3)類名是特性的名稱。
4)構造函數的參數是自定義特性的定位參數(應用該特性時必須放在參數列表的最前面),也能夠是無參構造函數(如[Serializable])。
5)任何公共的讀寫字段或屬性都是可選的命名參數。
6)若是特性類包含一個屬性,則該屬性必須爲讀寫屬性。
3、應用特性
示例代碼以下:
[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特性。
4、關聯特性
利用反射的原理,關聯特性類與目標類型(反射:主要利用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) { //一、判斷Users類定義時,是否應用了該特性? if (typeof(Users).IsDefined(typeof(PermissionAttribute),false)) { //二、得到該特性對象,以後就能夠訪問它的成員(元數據)。 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) { //一、判斷Users類定義時,是否應用了該特性? if (typeof(Users).IsDefined(typeof(PermissionAttribute), false)) { //二、得到該特性對象,以後就能夠訪問它的成員(元數據)。 PermissionAttribute attribute = (PermissionAttribute)Attribute.GetCustomAttribute(typeof(Users), typeof(PermissionAttribute)); if (attribute.compNo == "DB_SYSTEM") { return 1; } } return 0; } }
5、.NET預約義特性
至於.NET預約義特性的實現原理,我沒研究過,大概相似自定義特性吧,就好比系列化特性SerializableAttribute,實現原理我想大概是這樣:應用
[Serializable]時給目標作一個「標記」,在.NET內置程序集的某個地方判斷該目標類型是否應用了該特性,而後決定是否進行系列化操做。