C# Attribute 特性 學習

1、特性的概述
  1. 公共語言運行庫容許您添加相似關鍵字的描述性聲明(稱爲特性 (Attribute))來批註編程元素,如類型、字段、方法和屬性 (Property)。屬性與 Microsoft .NET Framework 文件的元數據一塊兒保存,而且可用於向運行庫描述代碼或影響應用程序的運行時行爲。
  2. 特性是一個對象,它能夠加載到程序集及程序集的對象中,這些對象包括 程序集自己、模塊、類、接口、結構、構造函數、方法、方法參,方法的返回值等,加載了特性的對象稱做特性的目標。特性是爲程序添加元數據(描述數據的數據)的一種機制,經過它能夠給編譯器提供指示或者提供對數據的說明。
  3. 爲運行庫編譯代碼時,該代碼被轉換爲 Microsoft 中間語言 (MSIL),並同編譯器生成的元數據一塊兒被放到可移植可執行 (PE) 文件的內部。屬性 (Attribute) 使您得以向元數據中放置額外的描述性信息,並可以使用運行庫反射服務提取該信息。
  4. 特性是一個對象,它是一個直接或間接繼承System.Attribute的類的對象,只是特性類的實例化和應用方式有點特別,下面會詳細說來!
2、應用特性
  1. 經過從 .NET Framework 導入屬性的命名空間來定義新的屬性或使用現有屬性。 例如:
              [Obsolete("請使用新的SendMsg(Message msg)重載方法")]
              public static void ShowMsg() {
                     Console.WriteLine("這是舊的SendMsg()方法");
              }
    Obsolete特性是一個系統特性,標記再也不使用的程序元素。
  2. 經過在緊鄰代碼元素以前放置屬性來將該屬性應用於代碼元素。 特性的使用方法:首先是有一對方括號「[]」,在左方括號「[」後緊跟特性的名稱,好比Obsolete,隨後是一個圓括號「()」。 和普通的類不一樣,這個圓括號不光能夠寫入構造函數的參數,還能夠給類的屬性賦值,在Obsolete的例子中,僅傳遞了構造函數參數。 當特性應用與程序集和模塊時,C#容許以指定的前綴來表示特性所應用的目標元素,建議這樣來處理,由於顯式處理能夠消除可能帶來的二義性。 例如:
    namespace Anytao.net  {
        [assembly: MyAttribute(1)]          //應用於程序集
        [moduel: MyAttribute(2)]            //應用於模塊
        pubic class Attribute_how2do     {         //略     } 
    }
  3. 爲屬性指定位置參數和命名參數。
    位置參數:構造函數的參數。 命名參數:屬性做爲參數。 使用構造函數參數,參數的順序必須同構造函數聲明時的順序相同,全部在特性中也叫位置參數(Positional Parameters),與此相應,屬性參數也叫作命名參數(Named Parameters)。位置參數是必需的,而且必須放在全部命名參數以前;
三編寫自定義
  1. 建立自定義類,類名以Attribute結尾(全部特性類都是以Attribute結尾,這是約定俗成的,這樣符合微軟的命名風格,也符合編譯器的搜索規則,但不是必須的),間接或者直接繼承System.Attribute類。
  2. 在咱們的特性類上應用AttributeUsage系統特性,用於指定咱們的自定義特性的應用目標類型。AttributeUsage特性的位置參數有一個是AttributeTargets枚舉位標記類型的,經過它來指定咱們的自定義特性的應用目標。另外還有兩個命名參數 AllowMultiple 是否能夠將特性重複應用到目標上;Inherited 我們自定義的特性類是否能夠被繼承。
  3. 聲明類屬性,用於存儲咱們要添加的元數據。經過構造函數賦值的屬性,設爲只讀(只有get訪問器),其它則爲讀寫屬性。這裏這樣作的目的是,在特性使用的時候,構造函數參數也就是位置參數是必需提供的,而命名參數,也就是爲讀寫屬性賦值是可選的,你將若是將位置參數設爲讀寫屬性,那它也將出如今命名參數中。
  4. 聲明構造函數,爲只讀屬性賦值。固然你也能夠在裏面聲明一些方法,只是方法在應用特性的時候無法體現。也能夠像普通自定義類同樣,調用類的方法。
4、自定義特性範例
假設咱們有這樣一個很常見的需求:咱們在建立或者更新一個類文件時,須要說明這個類是何時、由誰建立的,在之後的更新中還要說明在何時由誰更新的,能夠記錄也能夠不記錄更新的內容,以往你會怎麼作呢?是否是像這樣在類的上面給類添加註釋:
//更新:Joey, 2013-07-23
//更新:Joey, 2013-07-23,修改bug
//建立:zoe, 2013-07-15
public class DemoClass{
  // Class Body
}
這樣的的確確是能夠記錄下來,可是若是有一天咱們想將這些記錄保存到數據庫中做以備份呢?你是否是要一個一個地去查看源文件,找出這些註釋,再一條條插入數據庫中呢? 經過上面特性的定義,咱們知道特性能夠用於給類型添加元數據,這些元數據能夠用於描述類型。那麼在此處,特性應該會派上用場。那麼在本例中,元數據應該是:註釋類型(「更新」或者「建立」),修改人,日期,備註信息(無關緊要)。實現代碼以下:
 1 namespace Common.Library.ChangeRecord
 2 {
 3     /// <summary>
 4     /// desc:記錄 修改記錄 的特性
 5     /// </summary>
 6     [AttributeUsage((AttributeTargets)6140, AllowMultiple = true, Inherited = false)]
 7     public class RecordAttribute : Attribute
 8     {
 9         private RecordType recordType;      // 記錄類型:更新/建立
10         private string author;          // 做者
11         private DateTime date;          // 更新/建立 日期
12         private string mark;         // 備註
13  
14         // 構造函數,構造函數的參數在特性中也稱爲「位置參數」。
15         public RecordAttribute(RecordType recordType, string author, string date)
16         {
17             this.recordType = recordType;
18             this.author = author;
19             this.date = Convert.ToDateTime(date);
20         }
21  
22         // 對於位置參數,一般只提供get訪問器
23         public RecordType RecordType { get { return recordType; } }
24         public string Author { get { return author; } }
25         public DateTime Date { get { return date; } }
26  
27         // 構建一個屬性,在特性中也叫「命名參數」
28         public string Mark
29         {
30             get { return mark; }
31             set { mark = value; }
32         }
33     }
34     /// <summary>
35     /// 記錄類型 枚舉
36     /// </summary>
37     public enum RecordType
38     {
39         Add,
40         Modify
41     }
42 }
View Code
 
NOTE:注意構造函數的參數 date,必須爲一個常量、Type類型、或者是常量數組,因此不能直接傳遞DateTime類型。
這個類看上去和普通的類沒有和大區別,顯然不能它由於名字後面跟了個Attribute就搖身一變成了特性。它知足了,咱們前面所說的自定義特性的要求,繼承了System.Attribute類、用AttributeUsage特性限制了應用的目標、有屬性和構造函數等。
下面是應用特性:
 1 namespace Common.Library
 2 {
 3     [Record(RecordType.Add, "Joey", "2013-07-23")]
 4     [Record(RecordType.Modify, "Joey", "2013-07-23", Mark = "測試")]
 5     [Record(RecordType.Modify, "Joey", "2013-07-24", Mark = "修改bug")]
 6     public class TestAttribute
 7     {
 8         [Record(RecordType.Add, "Joey", "2013-07-23", Mark = "測試特性附加到方法上")]
 9         [Record(RecordType.Add, "Shovy", "2013-07-25", Mark = "修改此方法")]
10         public void MyMethod()
11         {
12         }
13  
14         [Record(RecordType.Add, "Joey", "2013-07-23", Mark = "方法2")]
15         [Record(RecordType.Add, "Zoe", "2013-07-25", Mark = "修改方法2")]
16         public void Method2()
17         {
18         }
19     }
20 }
View Code
 
這樣一個自定義特性就寫好了,也應用到目標元素上了。前面咱們說了特性是給目標類型添加描述性的元數據,它不會影響代碼的運行。上面代碼編譯後,咱們特性所添加的信息已經做爲元數據添加到了程序集中。能夠經過IL DASM看到:
 
5、使用反射查看自定義特性

特性通常是和反射結合使用的,你只附加了特性你不去獲取它,也沒有任何意義。而獲取類型上的特性信息,是經過反射來實現的。在這以前咱們有必要先了解下System.Attribute這個特性基類。
System.Attribute:
  1. protected Attribute(): 保護的構造器,只能被Attribute的派生類調用。
  2. 三個靜態方法: static Attribute GetCustomAttribute():這個方法有8種重載的版本,它被用來取出施加在類成員上指定類型的Attribute。 static Attribute[] GetCustomAttributes(): 這個方法有16種重載版本,用來取出施加在類成員上指定類型的Attribute數組。 static bool IsDefined():由八種重載版本,看是否指定類型的定製attribute被施加到類的成員上面。
  3. 實例方法: bool IsDefaultAttribute(): 若是Attribute的值是默認的值,那麼返回true。 bool Match():代表這個Attribute實例是否等於一個指定的對象。
  4. 公共屬性: TypeId: 獲得一個惟一的標識,這個標識被用來區分同一個Attribute的不一樣實例。 咱們簡單地介紹了Attribute類的方法和屬性,還有一些是從object繼承來的。這裏就不列出來了。
下面的代碼是檢測上面咱們自定義的特性的應用:
 
  
 1 namespace ConsoleApp
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             TestAttribute testClass = new TestAttribute();
 8             Type type = testClass.GetType(); //獲取要得到自定義特性的類型的Type對象
 9             Attribute[] myAttributes = Attribute.GetCustomAttributes(type);//獲取類型上添加的自定義特性
10             Console.WriteLine("類型{0}上應用的Record特性:", type.ToString());
11             Console.WriteLine();
12             foreach (Attribute item in myAttributes)
13             {
14                 if (item.GetType() == typeof(RecordAttribute)) //只打印咱自定義的特性
15                 {
16                     RecordAttribute attr = (RecordAttribute)item;
17                     Console.WriteLine("  類型:{0},更改人:{1},更改時間:{2},備註:{3}", attr.RecordType, attr.Author, attr.Date.ToString("yyyy-MM-dd"), attr.Mark);
18                 }
19             }
20             Console.WriteLine();
21             Console.WriteLine("類型{0}的方法上應用的Record特性:", type.ToString());
22             Console.WriteLine();
23             foreach (MethodInfo mInfo in type.GetMethods()) //遍歷該類型的全部方法
24             {
25                 if (Attribute.IsDefined(mInfo, typeof(RecordAttribute))) //只有在方法上附加了屬性,才遍歷
26                 {
27                     Console.WriteLine("  類型{0}的{1}方法上的Record特性", type.ToString(), mInfo.ToString());
28                     Console.WriteLine();
29                     foreach (Attribute item in Attribute.GetCustomAttributes(mInfo)) //遍歷方法上的 特性
30                     {
31                         if (item.GetType() == typeof(RecordAttribute))
32                         {
33                             RecordAttribute attr = (RecordAttribute)item;
34                             Console.WriteLine("    類型:{0},更改人:{1},更改時間:{2},備註:{3}", attr.RecordType, attr.Author, attr.Date.ToString("yyyy-MM-dd"), attr.Mark);
35                         }
36                     }
37                     Console.WriteLine();
38                 }
39             }
40         }
41     }}
View Code
 
運行效果以下圖所示:

獲取自定義特性的方法二:
首先基於類型(本例中是DemoClass)獲取一個Type對象,而後調用Type對象的GetCustomAttributes()方法,獲取應用於該類型上的特性。
當指定GetCustomAttributes(Type attributeType, bool inherit) 中的第一個參數attributeType時,
將只返回指定類型的特性,不然將返回所有特性;第二個參數指定是否搜索該成員的繼承鏈以查找這些屬性。
代碼以下:
 1 namespace ConsoleApp
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             TestAttribute testClass = new TestAttribute();
 8             Type type = testClass.GetType();
 9             object[] records = type.GetCustomAttributes(typeof(RecordAttribute), false); //獲取類型上的特性
10             Console.WriteLine("類型{0}上應用的Record特性:", type.ToString());
11             Console.WriteLine();
12             foreach (RecordAttribute record in records)
13             {
14                 Console.WriteLine("  類型:{0},更改人:{1},更改時間:{2},備註:{3}", record.RecordType, record.Author, record.Date.ToString("yyyy-MM-dd"), record.Mark);
15             }
16             Console.WriteLine();
17             Console.WriteLine("類型{0}的方法上應用的Record特性:", type.ToString());
18             Console.WriteLine();
19             foreach (MethodInfo mInfo in type.GetMethods()) //遍歷該類型的全部方法
20             {
21                 if (Attribute.IsDefined(mInfo, typeof(RecordAttribute))) //只有在方法上附加了屬性,才遍歷
22                 {
23                     Console.WriteLine("  類型{0}的{1}方法上的Record特性", type.ToString(), mInfo.ToString());
24                     Console.WriteLine();
25                     foreach (RecordAttribute record in mInfo.GetCustomAttributes(typeof(RecordAttribute), false)) //遍歷方法上的 特性
26                     {
27                         Console.WriteLine("    類型:{0},更改人:{1},更改時間:{2},備註:{3}", record.RecordType, record.Author, record.Date.ToString("yyyy-MM-dd"), record.Mark);
28                     }
29                     Console.WriteLine();
30                 }
31             }
32         }
33     }
34 }
View Code
 
兩種方法運行的結果是同樣的,如上圖。這兩種方法的區別在與,第一種方法是用的Atrribute基類的靜態方法獲取對應類型的自定義特性的集合。而第二種方案是使用的Type類型的方法獲取的。
demo下載:點擊這裏下載代碼
資料:

http://www.cnblogs.com/JimmyZhang/archive/2008/01/27/1055254.htmlhtml

Attribute在.net編程中的應用(一)
Attribute在.net編程中的應用(二)
Attribute在.net編程中的應用(三)
Attribute在.net編程中的應用(四)
Attribute在.net編程中的應用(五)
相關文章
相關標籤/搜索