C#特性之前的時候,用過C#中的特性,但只是會用,什麼原理,有什麼用這些問題不清楚,今天就騰出時間,學習了一下。html
C#中的特性使用Attribute描述。在使用時,就像是java中的批註同樣。不過C#使用中括號。特性用來描述咱們的數據。編譯器可以識別這些特性,以附加信息的形式存放在生成的元數據中,供clr使用。java
下邊看一個簡單的應用view plainprint?數組
static void Main(string[] args)ide
{ DisplayRunningMsg();DisplayDebugMsg();Trace("方法執行到結尾了!!");Console.Read();函數
} [DllImport("User32.dll")] public static extern int MessageBox(int hParent, string msg, string Caption, int type);[Conditional("DEBUG")] private static void DisplayRunningMsg()學習
{ Console.WriteLine("This is debug");Console.WriteLine("開始運行Main子程序。當前時間是"+DateTime.Now);}this
[Conditional("DEBUG")] [Obsolete] private static void DisplayDebugMsg()debug
{ Console.WriteLine("該方法已經廢棄啦!!!");}調試
DllImport特新容許咱們引入一個外部的dll,下邊作一個函數的聲明,咱們就能夠調用了。htm
Conditional屬性表示在該種條件下就執行下邊的代碼 因此[Conditional("DEBUG")]此種標識的方法就只有在調試的時候纔會在執行。 [Obsolete]特性標記該方法已經廢棄。
運行上述代碼輸出(在debug模式下)
看的出來程序執行了[Conditional("DEBUG")]標記的方法。若是咱們debug改成release,那麼再次執行
程序並無執行上述方法。看的出來,因爲特性[Conditional("DEBUG")]標記,是的在release模式下,代碼並無運行其標記的函數。那麼,咱們就能夠利用這個作一個error trace,使其只在debug的模式下輸出當前錯誤信息,包括行號,,方法名,位置等。這裏要用到 stacktrace類。
Ok,說到這裏,你應該對特性有了以最最基本的瞭解。
那麼,究竟什麼是特性呢?
其實特性也是一個類。好比[Conditional("DEBUG")],就是構造了以Conditional對象(調用構造方法public Conditional(string type), 對,DllImport("User32.dll")對應的也有一個類DllImport.下邊咱們自定義一個特性,你就會明白不少。
首先須要定一個一個類 ,該類須要集成Attribute,使其成爲一個特性……NET約定特性類都已Attribute結尾。而後在該類中定義一下字段和屬性,完成構造。
代碼以下view plainprint?
[AttributeUsage(AttributeTargets.All,AllowMultiple = true,Inherited = true)] class TrackerAttribute:Attribute {
private string opUsername;private string opName;private DateTime dateTime;private string note;
public TrackerAttribute(string opUsername,string opName,string date)
{ this.opUsername = opUsername;this.opName = opName;this.dateTime = DateTime.Parse(date);}
//位置參數,經過構造函數傳遞值public string OpUsername { get { return opUsername; } }
public string OpName { get { return opName; } }
public DateTime DateTime { get { return dateTime; } }
//命名參數,提供set public string Note { get { return note; } set { note = value; } }
public override string ToString()
{ return "操做人" + opUsername + "操做名" + opName + "時間" + dateTime + "備註" + note;}
嗯,對,他和普通的類幾乎沒什麼差異,只不過繼承於Attribute.而後他自己又有一些特性。咱們作逐一介紹咱們在類TrackerAttribute 定義了幾個字段,完成了構造函數TrackerAttribute(string opUsername,string opName,stringdate)
那麼我麼在使用的時候就須要寫[Tracker(「opusername」,「opname」,「2011-10-2600:04」,note=「這是備註」)],嗯,是的,使用類型(參數值,參數值)的方法完成了該對象的構造,即調用了該類的構造函數。構造函數裏與字段對應的參數叫作位置參數,由於寫的時候必須位置一一與源構造函數相同,其餘不經過構造函數傳入參數傳遞的,叫作命名參數,使用字段名=字段值 的形式賦值。這樣完成函數構造和一些屬性的賦值。通常狀況下,咱們將位置參數提供get訪問,而命名參數則提供get和set,由於位置參數已經可以同感哦構造函數訪問賦值了。
這個特性類上邊還有幾個特性,AttributeTargets表示當前特性的做用範圍,他是一個位標記的枚舉,好比all,field,method,標記事後,智能在相應的地方作該特性書寫。好比指定枚舉是做用與字段,那麼若是該特性寫在類上邊,就會報錯。
如上,你的特性類就完成了。
這樣你就能夠在其餘方法上作該特性的標記了。
咱們定義了特性,最重要的仍是要得到該特性中的值。下邊是得到的方法view plainprint?
Type type = typeof(Program);object[] objects = type.GetCustomAttributes(false);foreach (var o in objects)
{ TrackerAttribute trackerAttribute = o as TrackerAttribute;if (trackerAttribute != null)
Console.WriteLine(trackerAttribute.ToString());else { Console.WriteLine("得到對象爲空");}
type.GetCustomAttributes(false);該方法將會得到該類上的全部特性標記,返回的是一個object的數組,你能夠遍歷,而後轉換爲你的指定特性,訪問相應字段便可。一樣,你也能夠經過type.getMethods()[0] 得到一個methodinfo對象,而後調用該methodinfo對象的GetCustomAttributes方法便可。
介紹瞭如上的這些,咱們利用特性,實現爲枚舉增長一個得到其描述的功能。
例如定義枚舉MyEnummyenum=MyEnum.TypeA 調用myenum.ToDescription 能夠獲得字符串 類型A.咱們能夠想到定一個描述特性,而後在各個枚舉元素上,作該特性的標記,而後提供擴展方法,訪問該特性,取得該特性值。
代碼以下枚舉定義view plainprint?
public enum MyType { [Description("A類型")] TypeA,[Description("B類型")] TypeB,[Description("C類型")] TypeC }
特性類DescriptionAttribute定義以下view plainprint?
[AttributeUsage(AttributeTargets.Field,AllowMultiple =true,Inherited = true)] class DescriptionAttribute:Attribute { private string description;public string Description { get { return description; } }
public DescriptionAttribute(String description)
{ this.description = description;}
指定該特性只用於字段,定義DescriptionAttribute(String description)構造函數。
Ok,如今還缺乏枚舉的ToDescription方法,C#中的枚舉是不支持定義方法的。
咱們能夠爲其作一個擴展方法擴展方法須要一個靜態類,參數前要加this ,同時也指定了被擴展的對象,調用時可以使用擴展對像的實例調用,也可使用該靜態類來調用。詳細內容可參考http://www.cnblogs.com/sunrack/articles/1073759.html
擴展類以下view plainprint?
public static class Extension {
public static string ToDescription(this MyType myEnum)
{ Type type = typeof (MyType);FieldInfo info= type.GetField(myEnum.ToString());DescriptionAttribute descriptionAttribute= info.GetCustomAttributes(typeof (DescriptionAttribute), true)[0] as DescriptionAttribute;if (descriptionAttribute != null)
return descriptionAttribute.Description;else return type.ToString();}
這樣MyType就多了一個ToDescription的方法,返回的值就是對應的特性值。
在main方法中MyTypemyType = MyType.TypeB;Console.WriteLine(myType.ToDescription());控制檯輸出 B類型 達到了咱們想要的效果。
這個方法仍是頗有用的。
從上邊能夠看出,咱們若是要爲一些類添加一些附加的信息,1. 這些附加信息在現實意義與該對象並非具備真正的對象與屬性關係,2. 沒法在原來的,裏邊添加字段,或者加入字段後很難處理。這兩種狀況之一,均可以使用特性。隨後,在IL中看一下,掉用特性時,編譯器都作了什麼事。