約定:css
1.」attribute」 和 」attributes」 均不翻譯 html
2.」property」 譯爲「屬性」 c#
3.msdn 中的原句不翻譯 ide
4.」program entity」 譯爲 」 語言元素 」 函數
Attributes in C# 學習
介紹 this
Attributes 是一種新的描述信息,咱們既可使用 attributes 來定義設計期信息(例如 幫助文件,文檔的 URL ),還能夠用 attributes 定義運行時信息(例如,使 XML 中的元素與類的成員字段關聯起來)。咱們也能夠用 attributes 來建立一個「自描述」的組件。在這篇指南中咱們將明白怎麼建立屬性並將其綁定至各類語言元素上,另外咱們怎樣在運行時環境下獲取到 attributes 的一些信息。 spa
定義 翻譯
MSDN 中作以下定義 (ms-help://MS.MSDNQTR.2002APR.1033/csspec/html/vclrfcsh ARP spec_17_2.htm) 設計
"An attribute is a piece of additional declarative information that is specified for a declaration."
使用預約義 Attributes
在 c# 中已有一小組預約義的 attributes ,在咱們學習怎樣建立自定義 attributes 前,先來了解下在咱們的代碼中使用那些預約義的 attributes.
1 using System; 2 3 public class AnyClass 4 5 { 6 [Obsolete( " Don't use Old method, use New method " , true )] 7 8 static void Old( ) { } 9 10 static void New( ) { } 11 12 public static void Main( ) 13 { 14 Old( ); 15 } 16 }
仔細看下該實例,在該實例中咱們用到了 」Obsolete」attribute ,它標記了一個不應再被使用的語言元素 ( 譯者注:這裏的元素爲方法 ) ,該屬性的第一個參數是 string 類型,它解釋爲何該元素被荒棄,以及咱們該使用什麼元素來代替它。實際中,咱們能夠書寫任何其它文原本代替這段文本。第二個參數是告訴編譯器把依然使用這被標識的元素視爲一種錯誤,這就意味着編譯器會所以而產生一個警告。
當咱們試圖編譯上面的上面的程序,咱們會獲得以下錯誤:
AnyClass.Old()' is obsolete: 'Don't use Old method, use New method'
開發自定義 Attributes
如今咱們即將瞭解怎麼開發自定義的 attributes 。這兒有個小小處方,有它咱們就能夠學會建立自定義的 attributes 。
在 C# 中,咱們的 attribute 類都派生於 System.Attribute 類 ( A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration ) ,咱們就這麼行動吧。
using System; public class HelpAttribute : Attribute { }
無論你是否相信我,就這樣咱們就已經建立了一個自定義 attribute 。如今就能夠用它來裝飾咱們的類了,就像咱們使用 obsolete attribute 同樣。
[Help()] public class AnyClass { }
注意:按慣例咱們是用 」Attribute「 做爲 attribute 類名的後綴,然而,當咱們當咱們把 attribute 綁定到某語言元素時,是不包含 「Attribute「 後綴的。編譯器首先在 System.Attribute 的繼承類中查找該 attribute ,若是沒有找到,編譯器會把 「Attribute「 追加到該 attribute 的名字後面,而後查找它。
可是迄今爲止,該 attribute 沒有任何用處。爲了使它有點用處,讓咱們在它裏面加點東西吧。
using System; public class HelpAttribute : Attribute { public HelpAttribute(String Descrition_in) { this .description = Description_in; } protected String description; public String Description { get { return this .description; } } } [Help( " this is a do-nothing class " )] public class AnyClass { }
在上面的例子中,咱們在 attribute 類中添加了一個屬性,在最後一節中,咱們將在運行時查詢該屬性。
定義或控制自定義 Attribute 的用法
AttributeUsage 類是另外一預約義類 ( 譯者注: attribute 類自己用這個 atrribute System.AttributeUsage 來標記 ) ,它將幫助咱們控制咱們自定義 attribute 的用法,這就是,咱們能爲自定義的 attribute 類定義 attributes 。
它描述了一個自定義 attribute 類能被怎樣使用。
AttributeUsage 提供三個屬性,咱們能將它們放置到咱們的自定義 attribute 類上, 第一個特性是:
ValidOn
經過這個屬性,咱們能指定咱們的自定義 attribute 能夠放置在哪些語言元素之上。這組咱們能把自定義 attribute 類放置其上的語言元素被放在枚舉器 AttributeTargets 中。咱們可使用 bitwise( 譯者注:這個詞不知道怎麼翻譯好,但他的意思是能夠這麼用 : [AttributeUsage ( ( AttributeTargets)4 , AllowMultiple = false , Inherited = false )], 4 表明就是 「 class 」 元素,其它諸如 1 表明「 assembly 」, 16383 表明「 all 」等等 ) 或者 」.」 操作符綁定幾個 AttributeTargets 值。 (譯者注:默認值爲 AttributeTargets.All )
AllowMultiple
該屬性標識咱們的自定義 attribte 能在同一語言元素上使用屢次。 ( 譯者注:該屬性爲 bool 類型,默認值爲 false ,意思就是該自定義 attribute 在同一語言元素上只能使用一次 )
Inherited
咱們可使用該屬性來控制咱們的自定義 attribute 類的繼承規則。該屬性標識咱們的自定義 attribute 是否能夠由派生類繼承。( (譯者注:該屬性爲 bool 類型,默認值爲 false ,意思是不能繼承)
讓咱們來作點實際的東西吧,咱們將把 AttributeUsage attribute 放置在咱們的 help attribute 上並在它的幫助下,咱們來控制 help attribute 的用法。
using System; [AttributeUsage(AttributeTargets.Class, AllowMultiple = false , Inherited = false )] public class HelpAttribute : Attribute { public HelpAttribute(String Description_in) { this .description = Description_in; } protected String description; public String Description { get { return this .description; } } }
首先咱們注意 AttributeTargets.Class . 它規定這個 help attribute 只能放置在語言元素 」class」 之上。這就意味着,下面的代碼將會產生一個錯誤。
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
如今試着把它綁定到方法。
[Help( " this is a do-nothing class " )] public class AnyClass { [Help( " this is a do-nothing method " )] // error public void AnyMethod() { } }
咱們可使用 AttributeTargets.All 來容許 Help attribute 能夠放置在任何預約義的語言元素上,那些可能的語言元素以下 :
[Help( " this is a do-nothing class " )] [Help( " it contains a do-nothing method " )] public class AnyClass { [Help( " this is a do-nothing method " )] // error public void AnyMethod() { } }
它產生了一個編譯錯誤:
AnyClass.cs: Duplicate 'Help' attribute
Ok !如今咱們該討論下最後那個屬性了, 」Inherited」, 指出當把該 attribute 放置於一個基類之上,是否派生類也繼承了該 attribute 。若是綁定至某個 attribute 類的 」Inherited」 被設爲 true, 那麼該 attribute 就會被繼承,然而若是綁定至某個 attribute 類的 」Inherited」 被設爲 false 或者沒有定義,那麼該 attribute 就不會被繼承。
讓咱們假設有以下的類關係。
[Help( " BaseClass " )] public class Base { } public class Derive : Base { }
咱們有四種可能的綁定 :
第一種狀況
若是咱們查詢(咱們將在後面來了解如何在運行時來查詢 attributes )派生類中的 help attribute ,咱們將不可能查詢到由於 」Inherited」 被設爲了 false 。
第二種狀況
第二種狀況沒有什麼不一樣,由於其 」Inherited」 也被設爲了 false 。
第三種狀況
爲了解釋第三種和第四種狀況,讓咱們爲派生類也綁定同一 attribute 。
[Help( " BaseClass " )] public class Base { } [Help( " DeriveClass " )] public class Derive : Base { }
如今咱們查詢相關的 help attribute ,咱們將僅僅能夠獲得派生類的 attribute ,爲何這樣是由於 help attribute 雖然容許被繼承,但不能屢次在同一語言元素上使用,因此基類中的 help attribute 被派生類的 help attribute 重寫了。
第四種狀況
在第四種狀況中,當咱們查詢派生類的 help attribute 時,咱們能夠獲得兩個 attributes ,固然是由於 help attribute 既容許被繼承,又容許在同一語言元素上屢次使用的結果。
注意: AttributeUsage attribute 僅應用在那種是 System.Attribute 派生的 attriubte 類並且綁定值該 attriubte 類的 AllowMultiple 和 Inherited 均爲 false 上纔是有效的。
可選參數 vs. 命名參數
可選參數是 attribute 類構造函數的參數。它們是強制的,必須在每次在 attribute 綁定至某語言元素時提供一個值。而另外一方面,命名參數卻是真正的可選參數,不是在 attribute 構造函數的參數。
爲了更加詳細的解釋,讓咱們在 Help 類中添加另外的屬性。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false , Inherited = false )] public class HelpAttribute : Attribute { public HelpAttribute(String Description_in) { this .description = Description_in; this .verion = " No Version is defined for this class " ; } protected String description; public String Description { get { return this .description; } } protected String version; public String Version { get { return this .version; } // if we EVE r want our attribute user to set this property, // we must specify set method for it set { this .verion = value; } } } [Help( " This is Class1 " )] public class Class1 { } [Help( " This is Class2 " , Version = " 1.0 " )] public class Class2 { } [Help( " This is Class3 " , Version = " 2.0 " , Description = " This is do-nothing class " )] public class Class3 { }
當咱們在 Class1 中查詢 Help attribute 已經它的屬性,咱們將獲得:
Help.Description : This is Class1
Help.Version :No Version is defined for this class
由於咱們沒有爲 Version 這個屬性定義任何任何值,因此在構造函數中設定的值被咱們查詢出來了。若是沒有定義任何值,那麼就會賦一個該類型的默認值(例如:若是是 int 型,默認值就是 0 )。
如今,查詢 Class2 的結果是:
Help.Description : This is Class2
Help.Version : 1.0
咱們不能爲了可選參數而使用多個構造函數,應該用已命名參數來代替。咱們之因此稱它們爲已命名的,是由於當咱們在構造函數爲它們提供值時,咱們必須命名它們。例如,在第二個類中,咱們如是定義 Help 。
[Help( "This is Class2" , Version = "1.0" )]
在 AttributeUsage 例子中 , 參數 」ValidOn」 是可選參數,而 「Inherited「 和 「AllowMultiple「 是命名參數。
注意:爲了在 attribute 的構造函數中設定命名參數的值,咱們必須爲相應的屬性提供一個 set 方法不然會引發編譯期錯誤:
'Version' : Named attribute argument can't be a read only property
如今,咱們在 Class3 中查找 Help attribute 及其屬性會發生什麼呢?結果是跟上面提到的相同的編譯期錯誤。
'Desciption' : Named attribute argument can't be a read only property
如今咱們修改下 Help 類,爲屬性 」Description」 加一個 set 方法。如今的輸出就是:
Help.Description : This is do-nothing class
Help.Version : 2.0
在屏幕後面究竟發生了什麼呢?首先帶有可選參數的構造函數被調用,而後,每一個命名參數的 set 方法被調用,在構造函數中賦給命名參數的值被 set 方法所覆寫。
參數類型
一個 attribute 類的參數類型被限定在以下類型中:
Attributes 標記
假設,咱們想把 Help attribute 綁定至元素 assembly 。第一個問題是咱們要把 Help attribute 放在哪兒才能讓編譯器肯定該 attribute 是綁定至整個 assembly 呢?考慮另外一種狀況,咱們想把 attribute 綁定至一個方法的返回類型上,怎樣才能讓編譯器肯定咱們是把 attribute 綁定至方法的返回類型上,而不是整個方法呢?
爲了解決諸如此類的含糊問題,咱們使用 attribute 標識符,有了它的幫助,咱們就能夠確切地申明咱們把 attribute 綁定至哪個語言元素。
例如 :
[ assembly: Help( "this a do-nothing assembly" )]
這個在 Help attribute 前的 assembly 標識符確切地告訴編譯器,該 attribute 被綁定至整個 assembly 。可能的標識符有:
在運行時查詢 Attributes
如今咱們明白怎麼建立 attribtes 和把它們綁定至語言元素。是時候來學習類的使用者該如何在運行時查詢這信息。
爲了查詢一語言元素上綁定的 attributes ,咱們必須使用反射。反射有能力在運行時發現類型信息。
咱們可使用 .NET Framework Reflection APIs 經過對整個 assembly 元數據的迭代,列舉出 assembly 中全部已定義的類,類型,還有方法。
記住那舊的 Help attribute 和 AnyClass 類。
using System; using System.Reflection; using System.Diagnostics; // attaching Help attribute to entire assembly [assembly : Help( " This Assembly demonstrates custom attributes creation and their run - time query. " )] // our custom attribute class public class HelpAttribute : Attribute { public HelpAttribute(String Description_in) { // // TODO: Add constructor logic here this .description = Description_in; // } protected String description; public String Description { get { return this .deescription; } } } // attaching Help attribute to our AnyClass [HelpString( " This is a do-nothing Class. " )] public class AnyClass { // attaching Help attribute to our AnyMethod [Help( " This is a do-nothing Method. " )] public void AnyMethod() { } // attaching Help attribute to our AnyInt Field [Help( " This is any Integer. " )] public int AnyInt; } class QueryApp { public static void Main() { } }
咱們將在接下來的兩節中在咱們的 Main 方法中加入 attribute 查詢代碼。
查詢程序集的 Attributes
在接下來的代碼中,咱們先獲得當前的進程名稱,而後用 Assembly 類中的 LoadForm ()方法加載程序集,再有用 GetCustomAttributes ()方法獲得被綁定至當前程序集的自定義 attributes ,接下來用 foreach 語句遍歷全部 attributes 並試圖把每一個 attribute 轉型爲 Help attribute (即將轉型的對象使用 as 關鍵字有一個優勢,就是當轉型不合法時,咱們將不需擔憂會拋出異常,代之以空值( null )做爲結果),接下來的一行就是檢查轉型是否有效,及是否是爲空,跟着就顯示 Help attribute 的「 Description 」屬性。
class QueryApp { public static void Main() { HelpAttribute HelpAttr; // Querying Assembly Attributes String assemblyName; Process p = Process.GetCurrentProcess(); assemblyName = p.ProcessName + " .exe " ; Assembly a = Assembly.LoadFrom(assemblyName); foreach (Attribute attr in a.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of {0}:\n{1} " , assemblyName,HelpAttr.Description); } } } }
程序輸出以下:
Description of QueryAttribute.exe:
This Assembly demonstrates custom attributes creation and
their run-time query.
Press any key to continue
查詢類、方法、類成員的 Attributes
下面的代碼中,咱們唯一不熟悉的就是 Main ()方法中的第一行。
Type type = typeof (AnyClass);
它用 typeof 操做符獲得了一個與咱們 AnyClass 類相關聯的 Type 型對象。剩下的查詢類 attributes 代碼就與上面的例子是類似的,應該不要解釋了吧(我是這麼想的)。
爲查詢方法和類成員的 attributes, 首先咱們獲得全部在類中存在的方法和成員,而後咱們查詢與它們相關的全部 attributes ,這就跟咱們查詢類 attributes 同樣的方式。
class QueryApp { public static void Main() { Type type = typeof (AnyClass); HelpAttribute HelpAttr; // Querying Class Attributes foreach (Attribute attr in type.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of AnyClass:\n{0} " , HelpAttr.Description); } } // Querying Class-Method Attributes foreach (MethodInfo method in type.GetMethods()) { foreach (Attribute attr in method.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of {0}:\n{1} " , method.Name, HelpAttr.Description); } } } // Querying Class-Field (only public) Attributes foreach (FieldInfo field in type.GetFields()) { foreach (Attribute attr in field.GetCustomAttributes( true )) { HelpAttr = attr as HelpAttribute; if ( null != HelpAttr) { Console.WriteLine( " Description of {0}:\n{1} " , field.Name,HelpAttr.Description); } } } } }
The output of the following program is.
Description of AnyClass:
This is a do-nothing Class.
Description of AnyMethod:
This is a do-nothing Method.
Description of AnyInt:
This is any Integer.
Press any key to continue