[你必须知道的.NET] 第三回:历史纠葛:特性和属性
本文將介紹以下內容:
? 定制特性的基本概念和用法
? 屬性與特性的區別比較
? 反射的簡單介紹
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訪問器方法實現對可讀可寫屬性的操作,提供了安全和靈活的數據訪問封裝。關于屬性的概念,不是本文的重點,而且相信大部分的技術人員應該對屬性有清晰的概念。以下是簡單的屬性示例:
?
????public?class?MyProperty????{
????????//定義字段
????????private?string?_name;
????????private?int?_age;
????????//定義屬性,實現對_name字段的封裝
????????public?string?Name
????????{
????????????get?{?return?(_name?==?null)???string.Empty?:?_name;?}
????????????set?{?_name?=?value;?}
????????}
????????//定義屬性,實現對_age字段的封裝
????????//加入對字段的范圍控制
????????public?int?Age
????????{
????????????get?{?return?_age;?}
????????????set
????????????{
????????????????if?((value?>?0)?&&?(value?<?150))
????????????????{
????????????????????_age?=?value;
????????????????}
????????????????else
????????????????{
????????????????????throw?new?Exception("Not?a?real?age");
????????????????}
????????????}
????????}
????}
????public?class?MyTest
????{
????????public?static?void?Main(string[]?args)
????????{
????????????MyProperty?myProperty?=?new?MyProperty();
????????????//觸發set訪問器
????????????myProperty.Name?=?"Anytao";
????????????//觸發get訪問器
????????????Console.WriteLine(myProperty.Name);
????????????myProperty.Age?=?66;
????????????Console.WriteLine(myProperty.Age.ToString());
????????????Console.ReadLine();
????????}
????}
?
2.3. 區別與比較
通過對概念的澄清和歷史的回溯,我們知道特性和屬性只是在名稱上有過糾葛,在MSDN上關于attribute的中文解釋甚至還是屬性,但是我同意更通常的稱呼:特性。在功能上和應用上,二者其實沒有太多模糊的概念交叉,因此也沒有必要來比較其應用的異同點。本文則以特性的概念為重點,來討論其應用的場合和規則。
我理解的定制特性,就是為目標元素,可以是數據集、模塊、類、屬性、方法、甚至函數參數等加入附加信息,類似于注釋,但是可以在運行期以反射的方式獲得。定制特性主要應用在序列化、編譯器指令、設計模式等方面。
3. 通用規則
namespace?Anytao.net?
{
????[assembly:?MyAttribute(1)]??????????//應用于程序集
????[moduel:?MyAttribute(2)]????????????//應用于模塊
????pubic?class?Attribute_how2do
????{
????????//
????}?
}
?
AllowMultiple?=?true,?
Inherited?=?true]
class?MyNewAttribute:?System.Attribute
{
//
}?
?
4. 特性的應用
4.1. 常用特性
常用特性,也就是.NET已經提供的固有特性,事實上在.NET框架中已經提供了豐富的固有特性由我們發揮,以下精選出我認為最常用、最典型的固有特性做以簡單討論,當然這只是我的一家之言,亦不足道。我想了解特性,還是從這里做為起點,從.NET提供的經典開始,或許是一種求知的捷徑,希望能給大家以啟示。
AttributeUsage特性用于控制如何應用自定義特性到目標元素。關于AttributeTargets、AllowMultiple、Inherited、ValidOn,請參閱示例說明和其他文檔。我們已經做了相當的介紹和示例說明,我們還是在實踐中自己體會更多吧。
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"。關于位標記,也將在本系列的后續章回中有所交代,在此只做以探討止步。
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);
??????}
????}
}
Serializable特性表明了應用的元素可以被序列化(serializated),序列化和反序列化是另一個可以深入討論的話題,在此我們只是提出概念,深入的研究有待以專門的主題來呈現,限于篇幅,此不贅述。
Conditional特性,用于條件編譯,在調試時使用。注意:Conditional不可應用于數據成員和屬性。
還有其他的重要特性,包括:Description、DefaultValue、Category、ReadOnly、BrowerAble等,有時間可以深入研究。
4.2. 自定義特性
既然attribute,本質上就是一個類,那么我們就可以自定義更特定的attribute來滿足個性化要求,只要遵守上述的12條規則,實現一個自定義特性其實是很容易的,典型的實現方法為:
?
????[AttributeUsage(AttributeTargets.Class?|????????AttributeTargets.Method,
????????Inherited?=?true)]
????public?class?TestAttribute?:?System.Attribute
????{
????????public?TestAttribute(string?message)
????????{
????????????Console.WriteLine(message);
????????}
????????public?void?RunTest()
????????{
????????????Console.WriteLine("TestAttribute?here.");
????????}
????}
?
????????public?void?CannotRun()
????????{
????????????//
????????}
?
如果沒有什么機制來在運行期來獲取Attribute的附加信息,那么attribute就沒有什么存在的意義。因此,.NET中以反射機制來實現在運行期獲取attribute信息,實現方法如下:??
?
?
public?static?void?Main()????????{
????????????Tester?t?=?new?Tester();
????????????t.CannotRun();
????????????Type?tp?=?typeof(Tester);
????????????MethodInfo?mInfo?=?tp.GetMethod("CannotRun");????????????
????????????TestAttribute?myAtt?=?(TestAttribute)Attribute.GetCustomAttribute(mInfo,?typeof(TestAttribute));
????????????myAtt.RunTest();
????????}
?
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) 還可以記錄文件名或代碼作者,或在窗體開發階段控制控件和成員的可見性。
- dudu?Boss收藏的系列文章《Attribute在.net編程中的應用》,給你應用方面的啟示會很多,值得研究。
- 亞歷山大同志?的系列文章《手把手教你寫ORM(六)》中,也有很好的詮釋。
- idior的文章《Remoting基本原理及其擴展機制》也有收獲,因此補充。
6. 結論
?Attribute是.NET引入的一大特色技術,但在博客園中討論的不是很多,所以拿出自己的體會來分享,希望就這一技術要點進行一番登堂入室的引導。更深層次的應用,例如序列化、程序安全性、設計模式多方面都可以挖掘出閃耀的金子,這就是.NET在技術領域帶來的百變魅力吧。希望大家暢所欲言,來完善和補充作者在這方面的不全面和認知上的不深入,那將是作者最大的鼓勵和動力。
?
參考文獻
(USA)Stanley B.Lippman, C# Primer?
總結
以上是生活随笔為你收集整理的[你必须知道的.NET] 第三回:历史纠葛:特性和属性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 现在申请贷款买房,在什么情况下,等额本金
- 下一篇: 清华毕业生首份工作月薪2万起!机械工程专