本筆記摘抄自: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內置程式集的某個地方判斷該目標型別是否應用了該特性,然后決定是否進行系列化操作,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/63101.html
標籤:C#
