一、特性是什麽
前言
我們初學C#的時候看到類上面一對中括號裏面有個高亮了的關鍵字,不知道那是什麽有什麽用。想問人又不知道它叫什麽。糾結的要命。其實,它就是特性。如:
這就是我們今天要分析的主題。
特性是什麽?
個人理解,特性就是修飾對象元數據的修飾符。
那麽什麽是“元數據”?
元數據就是用來描述數據的數據。(挺拗口的)
如:
圖中的1.是特性 2.是訪問修飾符 3.聲明修飾符 4.數據類型 5.變量名 6.變量數據值,其中1、2、3、4、5就是元數據,用來描述數據(6)的數據。
特性到底是什麽?
如上面的 Obsolete ,會不會也是一個如 public static 這樣類似的修飾符呢,我們且看看反編譯後的中間語言。
意料之外,我們看到了上面的2、3、4、5,而1(特性)怎麽跑到裏面去了,且是一種看不懂的東東,反正我們知道了不是類似的修飾符。
然後我們接著在vs裏面把光標移到 Obsolete 上按F12,如:
原來只是一個繼承了 Attrbute 的一個類(class)。那麽上面我們看不懂的部分應該就是這個 ObsoleteAttribute 類的實例化了。
我們來回答上面問題:特性到底是什麽?特性只是一個類而已。
我們自定義一個特性玩玩
我們看到上面系統特性 Obsolete 上面還有特性,如:Serializable、AttributeUsage、Camvisible等。像這種特性我們稱之為“元數據的元數據”(元元數據)。
1.我們分別來解釋性上面的三個特性。
Serializable:表示類型支持序列化。
ComVisible:微軟定義“控制程序集中個別托管類型、 成員或所有類型對COM的可訪問性”。
AttributeUsage:這個比較重要了,基本上每個特性定義都用到了它。它就是用來表示當前這個特性可用於哪些對象。如:類、方法、屬性...等等。(只需要用到這個我們就可以自定義特性了)
2.上面有個問題,不知道大家發現沒有。
就是我們特性名明明是 Obsolete ,為什麽我們F12進去後變成了 ObsoleteAttribute 呢?這其實只是一個微軟的約定而已,沒有為什麽。
其實我們可以兩種寫法: [ObsoleteAttribute("已過時")] 和 [Obsolete("已過時")] 是等效的,只是我們一般都用後面這種。
3.定義的特性必須繼承於 Attribute 。
4.屬性沒有set方法。只能通過構造函數賦值。(這是因為特性語法所致,因為特性的定義只存在單行的中括號中,不能實例化之後在設置屬性,所以全部的設置都在後面的小括號裏進行的。如果需要有set屬性,我們就要用到命名參數,下面會繼續講到)
好了,我們通過這四點完全可以自己定義個特性來玩玩了。我們來定義一個給機器看的註釋。我們平時的註釋都只是給程序員看的,編譯之後就全沒了。那我們想在代碼運行時,彈出我們的註釋怎麽辦,接下來我們用自定義特性來實現,如:
[AttributeUsage(AttributeTargets.All)]//3.設置可用於哪些對象 public class TMessgAttribute : Attribute//1.定義類TMessg加上後綴TMessgAttribute 2.繼承Attribute。 { public TMessgAttribute() { } /// <param name="createTime">創建時間</param> /// <param name="createName">創建人</param> public TMessgAttribute(string createTime, string createName, string mess) { this._createName = createName; this._createTime = createTime; this._mess = mess; } private string _createTime; public string createTime { get { return _createTime; }//4.只能有get方法 } private string _createName; public string createName { get { return _createName; } } private string _mess; public string mess { get { return _mess; } } }
好了,上面就是我們自定義的特性。那我們怎樣使用呢。和系統特性一樣。我們先定義一個測試類TClass,然後在類上面定義特性,如:
[TMessg("2015-12-20", "zhaopei", "我只是測試自定義特性,不要報錯哦,求求你了。")] public class TClass { //................ }
我們定義了特性,也使用了特性,然我們卻不知道怎麽看效果。我們想看到效果怎麽辦。可以使用反射(下篇博問繼續分析反射)看看 TClass 類的元數據,如:
static void Main(string[] args) { System.Reflection.MemberInfo info = typeof(TClass); //通過反射得到TClass類的信息 TMessgAttribute hobbyAttr = (TMessgAttribute)Attribute.GetCustomAttribute(info, typeof(TMessgAttribute)); Console.WriteLine("類名:{0}", info.Name); Console.WriteLine("創建時間:{0}", hobbyAttr.createTime); Console.WriteLine("創建人:{0}", hobbyAttr.createName); Console.WriteLine("備註消息:{0}", hobbyAttr.mess); Console.ReadKey(); }
打印效果如:
什麽是命名參數?
上面的自定義特性都是通過構造函數設置字段私有字段,然後通過只提供了get的屬性來訪問。那麽可否直接在特性裏面定義擁有get和set的屬性嗎?答案是肯定的。那怎麽在使用特性的時候設置這個屬性呢?我們接著往下看。
我們接著在自定義特性裏面添加一個屬性。
/// <summary> /// 修改時間 /// </summary> public string modifyTime { get; set; }
使用自定義特性。
[TMessg("2015-12-20", "zhaopei", "我只是測試自定義特性,不要報錯哦,求求你了。", modifyTime = "2015-12-21")] public class TClass { //................ }
我們發現,直接在輸入了構造函數之後接著設置屬性就可以。(這就相當於可選參數了,屬性當然可以隨便你是否設置了。不過這裏需要註意了,前面的參數一定要按照定義的特性構造函數的參數順序)
這種參數,我們成為命名參數。
我們來繼續要看看AttributeUsage(這個描述特性的特性--“元元數據”)
我們F12看看AttributeUsage的定義
看上去,同樣也只是普通的特性。實際上也只是個普通的特性。>_<
我們來看看他的這幾個屬性是幹嘛的。從最後一個開始看。
1.AttributeTargets,我們在上面其實就已經看到並也已經使用了。
我們設置的是可用於所有對象。AttributeTargets其實是個枚舉,每個值對於一個類型對象。
你可以直接在 AttributeTargets F12進去:
我們看到了每個值代表可以用於所對於的對象類型。
2.Inherited(是一個布爾值):“如果該屬性可由派生類和重寫成員繼承,則為 true,否則為 false。 默認值為 true”
如下,我們設置 Inherited = false 那麽繼承TClass的T2Class無法訪問到TClass中設置的特性元數據。
View Code反之,我們設置 Inherited = true (或者不設置任何,因為默認就是true)打印如下:
3.AllowMultiple(也是一個布爾值):“如果允許指定多個實例,則為 true;否則為 false。 默認值為 false。”
我們設置兩個特性試試,如:
如果我們想要這樣設置怎麽辦。在AttributeUsage中設置 AllowMultiple = true 如:
那麽上面報錯的地方將會打印:
註意:上面的打印地方的代碼需要修改。因為之前是打印一個特性信息,這裏是打印一個特性數組集合的信息。
static void Main(string[] args) { System.Reflection.MemberInfo info = typeof(T2Class); TMessgAttribute[] hobbyAttr = (TMessgAttribute[])Attribute.GetCustomAttributes(info, typeof(TMessgAttribute));//修改1.這裏需要取特性數據的集合了 Console.WriteLine("類名:{0}", info.Name); for (int i = 0; i < hobbyAttr.Count(); i++)//修改2.這裏需要循環打印了 { Console.WriteLine("================================================"); Console.WriteLine("創建人:{0}", hobbyAttr[i].createName); Console.WriteLine("創建時間:{0}", hobbyAttr[i].createTime); Console.WriteLine("備註消息:{0}", hobbyAttr[i].mess); Console.WriteLine("修改時間:{0}", hobbyAttr[i].modifyTime); } Console.ReadKey();
全部代碼:
View Code自定義特性可以幹什麽?
上面我們通過反編譯,發現自定義特性實際上就是一個對象調用的最前面加了一段實例化的代碼。
那麽我們可以做的事可多了,除了像上面一樣為對象設置“註釋”,我們還可以自定義個特性,給某些方法或是某些類做“操作日誌記錄”,或者給需要在執行某些方法的時候需要權限,我們可以做個權限認證的特性等等。
這裏就需要大家自己去擴展了。
原文轉自:http://www.cnblogs.com/zhaopei/p/attribute_is_what.html
一、特性是什麽