国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

[CLR via C#]18. Attribute

2019-11-17 03:19:18
字體:
供稿:網(wǎng)友

[CLR via C#]18. Attribute

  attribute可以說是Microsoft .NET Framework提出的最具創(chuàng)意的技術之一了。利用attribute,可以聲明性的為自己的代碼構造添加注解,從而實現(xiàn)一些特殊的功能。attribute允許將定義的信息應用于幾乎每一個元數(shù)據(jù)表的記錄項。這種可擴展的元數(shù)據(jù)信息能在運行時查詢,從而動態(tài)改變代碼的執(zhí)行方式。  一、使用attribute  attribute可運用于類型和成員。Microsoft采取了一種機制提供對用戶自定義的attribute的支持。這種機制叫做定制attribute。  關于自定義attribute,首先應該知道:它們只是將一些附加信息與某個目標元素關聯(lián)起來的方式。編譯器會在托管模塊中生成這些額外的信息。  CLR允許將attribute應用于可在文件的元數(shù)據(jù)中表示的幾乎所有元素。不過,最常應用attribute的還是以下定義表中的記錄項:TypeDef(類、結(jié)構、枚舉、接口和委托),MethodDef(含構造器)、ParamDef(方法參數(shù))、FiledDef(字段)、PRopertyDef(屬性)、EventDef(事件),AssemblyDef(程序集)和MouduleDef(模塊)。  具體的說,在C#中只允許將attribute應用于對以下任何一個目標元素進行定義的源代碼:程序集、模塊、類型(類、結(jié)構、枚舉、接口、委托)、字段、方法(含構造器)、方法參數(shù)、方法返回值、屬性、事件和泛型類型參數(shù)。  應用一個attribute時,C#允許用一個前綴明確指定attribute要應用于的目標元素。以下代碼展示了所有可能的前綴。在許多情況下,即使省略前綴,編譯器一樣能判斷一個attribute要應用于的目標元素。但在另一些情況下,必須指定前綴向編譯器清楚表達我們的移除。下面傾斜顯示的前綴是必須的。
[assembly: MyAttr(1)]         // 應用于程序集[module: MyAttr(2)]           // 應用于模塊 [type: MyAttr(3)]             // 應用于類型internal sealed class SomeType<[typevar: MyAttr(4)] T> { // 應用于泛型類型變量    [field: MyAttr(5)]         // 應用于字段   public Int32 SomeField = 0;    [return: MyAttr(6)]        // 應用于返回值   [method: MyAttr(7)]        // 應用于方法   public Int32 SomeMethod(      [param: MyAttr(8)]      // 應用于方法參數(shù)      Int32 SomeParam) { return SomeParam; }    [property: MyAttr(9)]      // 應用于屬性   public String SomeProp {      [method: MyAttr(10)]    // 應用于get訪問器方法      get { return null; }   }    [event: MyAttr(11)]        // 應用于事件   [field: MyAttr(12)]        // 應用于編譯器生成的字段   [method: MyAttr(13)]       // 應用于編譯器生成的add&&remove方法   public event EventHandler SomeEvent;}  [AttributeUsage(AttributeTargets.All)]public class MyAttr : Attribute {   public MyAttr(Int32 x) { }} 

  前面介紹了如何應用一個attribute,接下來看看attribute到底是什么?

  attribute實際是一個類的實例。為了符合"公共語言規(guī)范"(CLS)的要求,attribute類必須直接或間接地從公共抽象類System.Attribute派生。C#只允許使用符合CLS規(guī)范的attribute。attribute類是可以在任何命名空間中定義的。

  如前所述,attribute是類的一個實例。類必須有一個公共構造器,這樣才能創(chuàng)建它的實例。所以,將一個attribute應用于一個目標元素時,語法類似于調(diào)用類的某個實例構造器。除此之外,語言可能支持一些特殊的語法,允許你設置于attribute類關聯(lián)的公共字段或?qū)傩浴1热纾覀儗llImport這個attribute應用于GetVersionEx方法:
[DllImport("kernel32",CharSet = CharSet.Auto, SetLastError = true)]

  這一行代碼語法表面上很奇怪,因為調(diào)用構造器時永遠不會出現(xiàn)這樣的語法。查閱DllImportAttbute類的文檔,會發(fā)現(xiàn)它只接受一個String類型的參數(shù)。在這個例子中,"Kernel32"這個String類型的參數(shù)已經(jīng)傳給它了。構造器的參數(shù)稱為"定位參數(shù)",而且是強制性的。也就是說,應用attribute時,必須指定參數(shù)。

  那么,另外兩個"參數(shù)"是什么?這種特殊的語法允許在DllImportAttbute對象構造好之后,設置對象的任何公共字段和屬性。在這個例子中,當DllImportAttbute對象構造好,而且將"Kernel32"傳給構造器之后,對象的公共實例字段CharSet和SetListError被分別設置為CharSet.Auto和true。用于設置字段或?qū)傩缘?參數(shù)"被稱為"命名參數(shù)"。這種參數(shù)是可選的,因為在應用attribute的一個實例時,不一定要指定命名參數(shù)。  另外,還可以將多個attribute應用于一個目標元素。將多個attribute應用一個目標元素時,attribute的順序是無關緊要的。在C#中,可將每個attribute都封閉到一對方括號中,也可以在一對方括號中封閉多個以逗號分隔的attribute。  二、定義自己的attibude類  現(xiàn)在我們已經(jīng)知道attribute是從System.Attribute派生的一個類的實例,并知道如何應用一個attribute。接著我們來研究下如何定制attribute。假定你是Microsoft的一位員工,并負責為枚舉類型添加位標志(biit flag)支持。為此,我們,要做的第一件事情是定義一個FlagAttribute類:namespace System {public class FlagsAttribute : System.Attribute {public FlagsAttribute(){}}}  注意,F(xiàn)lagsAttribute類是從Attribute繼承的。所以,才使FlagsAttribute類成為符合CLS要求的一個attribute。除此之外,注意類名有一個Attribute后綴;這是為了保持于標準的相容性,但并不是必須的。最后,所有的非抽象attribute都至少要包括一個公共構造器。  提示:attribute類型是一個類,但這個類應該非常簡單。這個類應該只提供一個公共構造器,它接受attribute的強制性狀態(tài)消息,而且這個類可以提供公共字段和屬性,以接受attribute的可選狀態(tài)信息。這個類不應提供任何公共方法、事件或其他成員。  三、attribute的構造器和字段/屬性的數(shù)據(jù)類型  定義一個attribute類時,可定義構造器來獲取參數(shù)。開發(fā)人員在應用該attribute類型的一個實例時,必須指定這些參數(shù)。除此之外,可在自己的類型中定義非靜態(tài)公共字段和屬性,使開發(fā)人員能夠為attribute類的實例選擇恰當?shù)脑O置。  定義attribute類的實例構造器、字段和屬性時,數(shù)據(jù)類型只能限制在一個小的子集內(nèi)。具體的說,合法的數(shù)據(jù)類型只有:Boolean,Char,Byte,Sbyte,Int16,UInt16,Int32,Uint32,Int64,Uint64,Single,Double,String,Type,Object或枚舉類型。除此之外,還可使用上述任意類型的一維0基數(shù)組。然而,要盡量避免使用數(shù)組,因為對于attribute類來說,如果它的構造器要獲取一個數(shù)組作為參數(shù),就會失去與CLS的相容性。  應用一個attribute時,必須傳遞一個編譯時常量表達式,它與attribute類定義的類型相匹配。在attribute類定義一個Type參數(shù),Type字段或者Type屬性的任何地方,都必須使用C#的typeof操作符。在attribute類定義一個Object參數(shù)、Object字段或Object屬性的任何地方,都可以傳遞一個Int32、String或者其他任何常量表達式(包括null)。如果常量表達式代表一個值類型,那么在運行時構造一個attribute的實例時,會對值類型進行裝箱。以下是一個示例attribute及用法:
public enum Color { Red } [AttributeUsage(AttributeTargets.All)]internal sealed class SomeAttribute : Attribute{    public SomeAttribute(String name, Object o, Type[] types)    {        // 'name'  引用了一個String類型        // 'o'     引用了一個合法類型(如有必要,就進行裝箱)        // 'types' 引用一個一維0基Type數(shù)組    }} [Some("Jeff", Color.Red, new Type[] { typeof(Math), typeof(Console) })]internal sealed class SomeType{}

  邏輯上,當編譯器檢測到一個目標元素應用了一個attribute時,編譯器會調(diào)用attribute類的構造器,向它傳遞任何指定的參數(shù),從而構造attribute類的一個實例。然后,編譯器會采用增強型構造器語法所指定的值,對任何公共字段和屬性進行初始化。在構造并初始化好定制attribute類的對象之后,編譯器會將這個attribute對象序列化到目標元素的元數(shù)據(jù)表記錄項中。

  提示:所謂"attribute",就是一個類的實例,它被序列化成駐留在元數(shù)據(jù)中的一個字節(jié)流。在運行時,可以對元數(shù)據(jù)中包含的字節(jié)進行反序列化,從而構造類的一個實例。實際發(fā)生的事情是:編譯器在元數(shù)據(jù)中生成創(chuàng)建attribute類的一個實例所需的信息。每個構造器參數(shù)都采取這樣的格式寫入:一個1字節(jié)長度的類型ID,后跟具體的值。在對構造器參數(shù)進行"序列化"之后,編譯器寫入字段/屬性名稱,后跟一個1字節(jié)的類型ID,再跟上具體的值,從而生成指定的每個字段和屬性的值。對于數(shù)組,會先保存數(shù)組元素的個數(shù),后跟每個單獨的元素。  四、檢測定制的attribute  可利用反射的技術來檢查attribute的存在。以后我們會完整探討這種技術。  假定你是Microsoft的員工,負責實現(xiàn)Enum的Format方法(Format方法會更根據(jù)是否有FlagsAttribute輸出不同的值),你會像下面這樣實現(xiàn)它:
public static String Format(Type enumType, Object value, String format) {        // 枚舉類型是否應用了FlagsAttrobute類型的一個實例    if(enumType.IsDefined(typeof(FlagsAttribute),false)){        //如果是,就執(zhí)行代碼,將值視為一個位標志枚舉類型        ...    }else {        // 如果不是,就執(zhí)行代碼,將值視為一個普通枚舉類型    }}

  上述代碼調(diào)用Type的IsDefined方法,要求系統(tǒng)查看枚舉類型的元數(shù)據(jù),檢查是否關聯(lián)了FlagsAttribute類的一個實例。如果IsDefined返回true,表面FlagsAttribute的一個實例已于枚舉類型關聯(lián),F(xiàn)ormat方法會認為值包含了一個位標志集合。如果IsDefined放回false,F(xiàn)ormat方法會認為值是一個普通的枚舉類型。

  定義定制attribute時,也必須實現(xiàn)一些代碼來檢查某個目標上是否存在該attribute類的實例,然后執(zhí)行一些邏輯分支代碼。正因為能做到這一點,定制attribute才如此有用。  FCL提供了多種方式檢查一個attribute的存在。我們知道所有于CLS相容的attribute都是從System.Attribute派生的。這個類定義了三個靜態(tài)方法來獲取與一個目標關聯(lián)的attribute:IsDefined,GetCustomAttribute和GetCustomAttributes。每個方法都有幾個重載版本。
方法名稱說明
IsDefined如果至少有一個指定的Attribute派生類的實例如目標關聯(lián),就放回true。這個方法效率很高,因為它不構造(反序列化)attribute類的任何實例
GetCustomAttribute返回引用于目標的指定attribute類的一個實例。實例使用編譯時指定的參數(shù)、字段和屬性來構造。如果目標沒有引用任何attribute類的實例,就返回null。如果目標應用了指定attribute的多個實例,就拋出異常。方法通常用于已將AllowMultiple設為false的attribute。
GetCustomAttributes返回一個數(shù)組,其中每個元素都是應用于目標的指定attribute類的一個實例。如果不為這個方法指定具體的attribute類,數(shù)組中包含的就是已應用的所有attribute的實例,不管它們是什么類。每個實例都使用編譯時指定的參數(shù)、字段和屬性來構造(反序列化)。如果目標沒有應用任何attribute類的實例,就返回一個空數(shù)組。該方法通常用于已將AllowMultiple設為true的attribute,或者用于列出已應用的所有attribute。
  如果只想知道一個attribute是否應用于一個目標,那么應該調(diào)用IsDefined,因為它的效率比另外兩個方法高的多。我們知道,將一個attribute應用于一個目標時,可以為attribute的構造器指定參數(shù),并可以選擇設置字段或?qū)傩浴J褂肐sDefined不會構造一個attribute對象,不會調(diào)用它的構造器,也不會設置它的字段和屬性。  每次使用GetCustomAttribute和GetCustomAttributes方法時,都會為構造attribute對象的新實例,并根據(jù)源代碼中指定的值來設置每個實例的字段和屬性。這個兩個方法返回的都是一個引用,執(zhí)行完全構造好的attribute類的實例。  調(diào)用上述任何一個方法時,它們內(nèi)部必須掃描托管模塊的元數(shù)據(jù),執(zhí)行字符串比較來定位指定的attribute類。顯然,這些操作會耗費一定的事件。假如對性能要求高,可以考慮緩存這些方法調(diào)用的返回結(jié)果。  System.Reflection 命名空間定義了幾個類允許你檢查一個模塊的元數(shù)據(jù)的內(nèi)容。這些類包括Assmbly,Module,ParameterInfo,MemberInfo,Type,MethodInfo,ConstructorInfo,FiledInfo,EeventInfo,PropertyInfo及其各自的*Builder類。所以這些方法還提供了IsDefined和GetCustomAttributes方法。只有System.Attribute提供了非常方便的GetCustomAttribute方法。  反射類提供的那個版本的GetCustomAttributes方法返回的是有Object實例構成的一個數(shù)據(jù)(Object[]),而不是由Attribute實例構成的一個數(shù)組(Attribute[])。  注意:將一個類傳給IsDefined,GetCustomAttribute或GetCustomAttributes方法時,這些方法會搜索指定的attribute類或它的派生類,如果代碼要搜索一個具體的attribute類,應該針對返回值執(zhí)行一個額外的檢查,確保這些方法返回的正是向搜索的莪累。還可以考慮將自己的attribute類定義成sealed,減少可能存在的混淆,并避免這個檢查。以下示例代碼列出了一個類型中定義的所有方法,并顯示應用于每個方法的attribute代碼。
[assembly: CLSCompliant(true)]     [Serializable]    [DefaultMemberAttribute("Main")]    [DebuggerDisplayAttribute("Richter", Name = "Jeff", Target = typeof(Program))]    public sealed class Program    {        [Conditional("Debug")]        [Conditional("Release")]        public void DoSomething() { }         public Program()        {        }          [assembly: CLSCompliant(true)]        [STAThread]        public static void Main()        {            // 顯示應用于這個類型的attribute集            ShowAttributes(typeof(Program));             // 獲取與類型關聯(lián)的方法集            MemberInfo[] members = typeof(Program).FindMembers(               MemberTypes.Constructor | MemberTypes.Method,               BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static,               Type.FilterNam
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 新巴尔虎右旗| 邢台市| 同江市| 池州市| 凤冈县| 青川县| 西藏| 绍兴市| 万盛区| 泸西县| 方山县| 乳源| 门源| 安溪县| 高邑县| 中方县| 樟树市| 类乌齐县| 鹤庆县| 蛟河市| 社旗县| 府谷县| 乌恰县| 迁安市| 会宁县| 屏东市| 高平市| 灵石县| 库车县| 静宁县| 上饶县| 岫岩| 泰和县| 太白县| 浦城县| 黑龙江省| 元阳县| 从化市| 江川县| 石渠县| 涪陵区|