1、引言
attribute是.NET框架引入的有一技術亮點,因此我們有必要花點時間來了解本文的內容,走進一個發現attribute登堂入室的入口。因爲.NET Framework中使用了大量的定製特性來完成代碼約定,[Serializable]、[Flags]、[DllImport]、[AttributeUsage]這些的構造,相信我們都見過吧!那麼你是否瞭解其背後的技術。
提起特性,由於高級語言發展的歷史原因,不免讓人想起另一個耳熟能詳的名字:屬性。特性和屬性,往往給初學者或者從C++轉移到C#的人混淆的概念衝擊。那麼,什麼是屬性,什麼是特性,二者的概念和區別,用法與示例,將在本文做以概括性的總結和比較,希望給你的理解帶來收穫。另外本文的主題以特性的介紹爲主,屬性的論述重點突出在二者的比較上,關於屬性的更多論述將在另一篇主題中詳細討論,敬請關注。
2、概念引入
2.1、什麼是特性?
MADN的定義爲:公共語言運行時允許添加類似關鍵字的描述聲明,叫做attributes, 它對程序中的元素進行標註,如類型、字段、方法和屬性等。Attributes和Microsoft .NET Framework文件的元數據保存在一起,可以用來向運行時描述你的代碼,或者在程序運行的時候影響應用程序的行爲。
我們簡單的總結爲:定製特性attribute,本質上是一個類,其爲目標元素提供關聯附加信息,並在運行期以反射的方式來獲取附加信息。具體的特性實現方法,在接下來的討論中繼續深入。
2.2、什麼是屬性?
屬性是面向對象編程的基本概念,提供了對私有字段的訪問封裝,在C#中以get和set訪問器方法實現對可讀可寫屬性的操作,提供了安全和靈活的數據訪問封裝。關於屬性的概念,不是本文的重點,而且相信大部分的技術人員應該對屬性有清晰的概念。以下是簡單的屬性示例:
2.3、區別與比較
通過對概念的澄清和歷史的回溯,我們知道特性和屬性只是在名稱上有過糾葛,在MSDN上關於attribute的中文解釋甚至還是屬性,但是我同意更通常的稱呼:特性。在功能上和應用上,二者其實沒有太多模糊的概念交叉,因此也沒有必要來比較其應用的異同點。本文則以特性的概念爲重點,來討論其應用的場合和規則。
我理解的定製特性,就是爲目標元素,可以是數據集、模塊、類、屬性、方法、甚至函數參數等加入附加信息,類似於註釋,但是可以在運行期以反射的方式獲得。定製特性主要應用在序列化、編譯器指令、設計模式等方面。
3、通用規則
(1)定製特性可以應用的目標元素可以爲:程序集(assembly)、模塊(module)、類型(type)、屬性(property)、事件(event)、字段(field)、方法(method)、參數(param)、返回值(return),應該全了。 (2)定製特性以[,]形式展現,放在緊挨着的元素上,多個特性可以應用於同一元素,特性間以逗號隔開,以下表達規則有效:[AttributeUsage][ Flags]、[AttributeUsage, Flags]、[Flags, AttibuteUsageAttribute]、[AttributeUsage(), FlagesAttribute()]。 (3)attibute實例,是在編譯期進行初始化,而不是運行期。 (4)C#允許以指定的前綴來表示特性所應用的目標元素,建議這樣來處理,因爲顯式處理可以消除可能帶來的二義性。例如:
using System; namespace Anytao.net { [assembly: MyAttribute(1)] //應用於程序集 [moduel: MyAttribute(2)] //應用於模塊 pubic class Attribute_how2do { // } }
|
#p#
(5)定製特性類型,必須直接或者間接的繼承自System.Attribute類,而且該類型必須有公有構造函數來創建其實例。 (6)所有自定義的特性名稱都應該有個Attribute後綴,這是習慣性約定。 (7)定製特性也可以應用在其他定製特性上,這點也很好理解,因爲定製特性本身也是一個類,遵守類的公有規則。例如很多時候我們的自定義定製特性會應用AttributeUsageAttribute特性,來控制如何應用新定義的特性。
[AttributeUsageAttribute(AttributeTarget.All), AllowMultiple = true, Inherited = true] class MyNewAttribute: System.Attribute { // }
|
(8)定製特性不會影響應用元素的任何功能,只是約定了該元素具有的特質。 (9)所有非抽象特性必須具有public訪問限制。 (10)特性常用於編譯器指令,突破#define,#undefine,#if,#endif的限制,而且更加靈活。 (11)定製特性常用於在運行期獲得代碼註釋信息,以附加信息來優化調試。 (12)定製特性可以應用在某些設計模式中,如工廠模式。 (13)定製特性還常用於位標記,非託管函數標記、方法廢棄標記等其他方面。
4、特性的應用
4.1、常用特性
常用特性,也就是.NET已經提供的固有特性,事實上在.NET框架中已經提供了豐富的固有特性由我們發揮,以下精選出我認爲最常用、最典型的固有特性做以簡單討論,當然這只是我的一家之言,亦不足道。我想了解特性,還是從這裏做爲起點,從.NET提供的經典開始,或許是一種求知的捷徑,希望能給大家以啓示。
1、AttributeUsage
AttributeUsage特性用於控制如何應用自定義特性到目標元素。關於AttributeTargets、AllowMultiple、Inherited、ValidOn,請參閱示例說明和其他文檔。我們已經做了相當的介紹和示例說明,我們還是在實踐中自己體會更多吧!
2、Flags
以Flags特性來將枚舉數值看作位標記,而非單獨的數值,例如:
enum Animal { Dog = 0x0001, Cat = 0x0002, Duck = 0x0004, Chicken = 0x0008 }
|
因此,以下實現就相當輕鬆,
Animal animals = Animal.Dog | Animal.Cat; Console.WriteLine(animals.ToString());
|
請猜測結果是什麼,答案是:“Dog, Cat”。如果沒有Flags特別,這裏的結果將是“3”。關於位標記,也將在本系列的後續章回中有所交代,在此只做以探討止步。
3、DllImport
DllImport特性,可以讓我們調用非託管代碼,所以我們可以使用DllImport特性引入對Win32 API函數的調用,對於習慣了非託管代碼的程序員來說,這一特性無疑是救命的稻草。
using System; using System.Runtime.InteropServices; namespace Anytao.net { class MainClass { [DllImport("User32.dll")] public static extern int MessageBox(int hParent, string msg, string caption, int type); static int Main() { return MessageBox(0, "How to use attribute in .NET", "Anytao_net", 0); } } }
|
4、Serializable
Serializable特性表明了應用的元素可以被序列化(serializated),序列化和反序列化是另一個可以深入討論的話題,在此我們只是提出概念,深入的研究有待以專門的主題來呈現,限於篇幅,此不贅述。
5、Conditional
Conditional特性,用於條件編譯,在調試時使用。注意:Conditional不可應用於數據成員和屬性。
還有其他的重要特性,包括:Description、DefaultValue、Category、ReadOnly、BrowerAble等,有時間可以深入研究。
4.2、自定義特性
既然attribute,本質上就是一個類,那麼我們就可以自定義更特定的attribute來滿足個性化要求,只要遵守上述的12條規則,實現一個自定義特性其實是很容易的,典型的實現方法爲:
#p#
1、定義特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)] public class TestAttribute : System.Attribute { public TestAttribute(string message) { throw new Exception("error:" + message); } public void RunTest() { Console.WriteLine("TestAttribute here."); } }
|
2、應用目標元素
[Test("Error Here.")] public void CannotRun() { // }
|
3、獲取元素附加信息
如果沒有什麼機制來在運行期來獲取Attribute的附加信息,那麼attribute就沒有什麼存在的意義。因此,.NET中以反射機制來實現在運行期獲取attribute信息,實現方法如下:
public static void Main(string[] args) { Tester t = new Tester(); t.CannotRun(); Type tp = typeof(Tester); TestAttribute myAtt = (TestAttribute)Attribute.GetCustomAttribute((MemberInfo)tp, typeof(TestAttribute)); }
|
5、經典示例
5.1、小菜一碟
啥也不說了,看註釋吧!
using System; using System.Reflection; //應用反射技術獲得特性信息 namespace Anytao.net { //定製特性也可以應用在其他定製特性上, //應用AttributeUsage,來控制如何應用新定義的特性 [AttributeUsageAttribute(AttributeTargets.All, //可應用任何元素 AllowMultiple = true, //允許應用多次 Inherited = false)] //不繼承到派生類 //特性也是一個類, //必須繼承自System.Attribute類, //命名規範爲:"類名"+Attribute。 public class MyselfAttribute : System.Attribute { //定義字段 private string _name; private int _age; private string _memo; //必須定義其構造函數,如果不定義有編譯器提供無參默認構造函數 public MyselfAttribute() { } public MyselfAttribute(string name, int age) { _name = name; _age = age; } //定義屬性 //顯然特性和屬性不是一回事兒 public string Name { get { return _name == null ? string.Empty : _name; } } public int Age { get { return _age; } } public string Memo { get { return _memo; } set { _memo = value; } } //定義方法 public void ShowName() { Console.WriteLine("Hello, {0}", _name == null ? "world." : _name); } } //應用自定義特性 //可以以Myself或者MyselfAttribute作爲特性名 //可以給屬性Memo賦值 [Myself("Emma", 25, Memo = "Emma is my good girl.")] public class Mytest { public void SayHello() { Console.WriteLine("Hello, my.net world."); } } public class Myrun { public static void Main(string[] args) { //如何以反射確定特性信息 Type tp = typeof(Mytest); MemberInfo info = tp; MyselfAttribute myAttribute = (MyselfAttribute)Attribute.GetCustomAttribute(info, typeof(MyselfAttribute)); if (myAttribute != null) { //嘿嘿,在運行時查看註釋內容,是不是很爽 Console.WriteLine("Name: {0}", myAttribute.Name); Console.WriteLine("Age: {0}", myAttribute.Age); Console.WriteLine("Memo of {0} is {1}", myAttribute.Name, myAttribute.Memo); myAttribute.ShowName(); } //多點反射 object obj = Activator.CreateInstance(typeof(Mytest)); MethodInfo mi = tp.GetMethod("SayHello"); mi.Invoke(obj, null); Console.ReadLine(); } } }
|
啥也別想了,自己做一下試試。
5.2、他山之石
MSDN認爲,特性 (Attribute) 描述如何將數據序列化,指定用於強制安全性的特性,並限制實時 (JIT) 編譯器的優化,從而使代碼易於調試。屬性 (Attribute) 還可以記錄文件名或代碼作者,或在窗體開發階段控制控件和成員的可見性。
6、結論
Attribute是.NET引入的一大特色技術,但討論的不是很多,所以拿出自己的體會來分享,希望就這一技術要點進行一番登堂入室的引導。更深層次的應用,例如序列化、程序安全性、設計模式多方面都可以挖掘出閃耀的金子,這就是.NET在技術領域帶來的百變魅力吧!希望大家暢所欲言,來完善和補充作者在這方面的不全面和認知上的不深入,那將是作者最大的鼓勵和動力。
【相關文章】
【責任編輯: 火鳳凰 TEL:(010)68476606】
|