轉自:http://www.cnblogs.com/-dawn/archive/2012/09/29/2708053.htmlcss
介紹
html
Attributes是一種新的描述信息,咱們既可使用attributes來定義設計期信息(例如幫助文件,文檔的URL),還能夠用attributes定義運行時信息(例如,使XML中的元素與類的成員字段關聯起來)。咱們也能夠用attributes來建立一個「自描述」的組件。在這篇指南中咱們將明白怎麼建立屬性並將其綁定至各類語言元素上,另外咱們怎樣在運行時環境下獲取到attributes的一些信息。c#
定義ide
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." 學習
使用預約義Attributesthis
在c#中已有一小組預約義的attributes,在咱們學習怎樣建立自定義attributes前,先來了解下在咱們的代碼中使用那些預約義的attributes.
spa
using System; publicclass AnyClass { [Obsolete( "Don't use Old method, use New method", true)] staticvoid Old( ) { } staticvoid New( ) { } publicstaticvoid Main( ) { Old( ); } }
仔細看下該實例,在該實例中咱們用到了」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;
publicclass HelpAttribute : Attribute
{
}
無論你是否相信我,就這樣咱們就已經建立了一個自定義 attribute。如今就能夠用它來裝飾咱們的類了,就像咱們使用obsolete attribute同樣。
[Help()]
publicclass AnyClass
{
}
注意:按慣例咱們是用」Attribute「做爲attribute類名的後綴,然而,當咱們當咱們把attribute綁定到某語言元素時,是不包含「Attribute「後綴的。編譯器首先在System.Attribute的繼承類中查找該attribute,若是沒有找到,編譯器會把「Attribute「追加到該attribute的名字後面,而後查找它。
可是迄今爲止,該attribute沒有任何用處。爲了使它有點用處,讓咱們在它裏面加點東西吧。
using System; publicclass HelpAttribute : Attribute { public HelpAttribute(String Descrition_in) { this.description = Description_in; } protected String description; public String Description { get { returnthis.description; } } [Help( "this is a do-nothing class")] publicclass 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,意思是不能繼承)
讓咱們來作點實際的東西吧,咱們將把AttributeUsageattribute 放置在咱們的help attribute 上並在它的幫助下,咱們來控制help attribute的用法。
using System; [AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited =false )] publicclass HelpAttribute : Attribute { public HelpAttribute(String Description_in) { this.description = Description_in; } protected String description; public String Description { get { returnthis.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")] publicclass AnyClass { [Help( "this is a do-nothing method")] //error publicvoid AnyMethod() { } }
咱們可使用 AttributeTargets.All來容許Help attribute 能夠放置在任何預約義的語言元素上,那些可能的語言元素以下:
Assembly,
Module,
Class,
Struct,
Enum,
Constructor,
Method,
Property,
Field,
EVE nt,
Interface,
Parameter,
Delegate,
All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | EVE nt | Interface | Parameter | Delegate,
ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | EVE nt | Delegate | Interface )
~ 如今考慮下AllowMultiple = false. 這就規定該attribute 不能在同一語言元素上放置屢次.
[Help("this is a do-nothing class")]
[Help( "it contains a do-nothing method")]
publicclass AnyClass
{
[Help( "this is a do-nothing method")] //error
publicvoid AnyMethod()
{
}
}
它產生了一個編譯錯誤:
AnyClass.cs: Duplicate 'Help' attribute
Ok!如今咱們該討論下最後那個屬性了,」Inherited」, 指出當把該attribute放置於一個基類之上,是否派生類也繼承了該attribute。若是綁定至某個attribute類的」Inherited」被設爲true,那麼該attribute就會被繼承,然而若是綁定至某個attribute類的」Inherited」被設爲false或者沒有定義,那麼該attribute就不會被繼承。
讓咱們假設有以下的類關係。
[Help("BaseClass")]
publicclass Base
{
}
publicclass Derive : Base
{
}
咱們有四種可能的綁定 :
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
第一種狀況
若是咱們查詢(咱們將在後面來了解如何在運行時來查詢attributes)派生類中的help attribute,咱們將不可能查詢到由於」Inherited」被設爲了false。
第二種狀況
第二種狀況沒有什麼不一樣,由於其」Inherited」也被設爲了false。
第三種狀況
爲了解釋第三種和第四種狀況,讓咱們爲派生類也綁定同一attribute。
[Help("BaseClass")]
publicclass Base
{
}
[Help( "DeriveClass")]
publicclass Derive : Base
{
}
如今咱們查詢相關的 help attribute ,咱們將僅僅能夠獲得派生類的attribute,爲何這樣是由於help attribute雖然容許被繼承,但不能屢次在同一語言元素上使用,因此基類中的help attribute被派生類的help attribute 重寫了。
第四種狀況
在第四種狀況中,當咱們查詢派生類的help attribute 時,咱們能夠獲得兩個attributes,固然是由於help attribute既容許被繼承,又容許在同一語言元素上屢次使用的結果。
注意:AttributeUsageattribute 僅應用在那種是System.Attribute派生的attriubte類並且綁定值該attriubte類的AllowMultiple和Inherited均爲false上纔是有效的。
可選參數vs. 命名參數
可選參數是attribute類構造函數的參數。它們是強制的,必須在每次在attribute綁定至某語言元素時提供一個值。而另外一方面,命名參數卻是真正的可選參數,不是在attribute構造函數的參數。
爲了更加詳細的解釋,讓咱們在Help類中添加另外的屬性。
[AttributeUsage(AttributeTargets.Class, AllowMultiple =false,
Inherited =false)]
publicclass 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
{
returnthis.description;
}
}
protected String version;
public String Version
{
get
{
returnthis.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")]
publicclass Class1
{
}
[Help( "This is Class2", Version ="1.0")]
publicclass Class2
{
}
[Help( "This is Class3", Version ="2.0",
Description ="This is do-nothing class")]
publicclass 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類的參數類型被限定在以下類型中:
bool,
byte,
char,
double,
float,
int,
long,
short,
string
System.Type
object
An enum type, provided that it and any types in which it is nested are publicly accessible. A one-dimensional array involving any of the types listed above
Attributes 標記
假設,咱們想把Help attribute 綁定至元素assembly。第一個問題是咱們要把Help attribute 放在哪兒才能讓編譯器肯定該attribute是綁定至整個assembly呢?考慮另外一種狀況,咱們想把attribute綁定至一個方法的返回類型上,怎樣才能讓編譯器肯定咱們是把attribute綁定至方法的返回類型上,而不是整個方法呢?
爲了解決諸如此類的含糊問題,咱們使用attribute標識符,有了它的幫助,咱們就能夠確切地申明咱們把attribute 綁定至哪個語言元素。
例如:
[ assembly: Help("this a do-nothing assembly")]
這個在Help attribute 前的assembly標識符確切地告訴編譯器,該attribute被綁定至整個assembly。可能的標識符有:
assembly
module
type
method
property
EVE nt
field
param
return
在運行時查詢Attributes
如今咱們明白怎麼建立attribtes和把它們綁定至語言元素。是時候來學習類的使用者該如何在運行時查詢這信息。
爲了查詢一語言元素上綁定的attributes,咱們必須使用反射。反射有能力在運行時發現類型信息。
咱們可使用.NET Framework Reflection APIs 經過對整個assembly元數據的迭代,列舉出assembly中全部已定義的類,類型,還有方法。
記住那舊的Helpattribute 和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
publicclass HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
//
// TODO: Add constructor logic here
this.description = Description_in;
//
}
protected String description;
public String Description
{
get
{
returnthis.deescription;
}
}
}
//attaching Help attribute to our AnyClass
[HelpString( "This is a do-nothing Class.")]
publicclass AnyClass
{
//attaching Help attribute to our AnyMethod
[Help( "This is a do-nothing Method.")]
publicvoid AnyMethod()
{
}
//attaching Help attribute to our AnyInt Field
[Help( "This is any Integer.")]
publicint AnyInt;
}
class QueryApp
{
publicstaticvoid Main()
{
}
}
咱們將在接下來的兩節中在咱們的 Main方法中加入attribute查詢代碼。
查詢程序集的Attributes
在接下來的代碼中,咱們先獲得當前的進程名稱,而後用Assembly類中的LoadForm()方法加載程序集,再有用GetCustomAttributes()方法獲得被綁定至當前程序集的自定義attributes,接下來用foreach語句遍歷全部attributes並試圖把每一個attribute轉型爲Help attribute(即將轉型的對象使用as關鍵字有一個優勢,就是當轉型不合法時,咱們將不需擔憂會拋出異常,代之以空值(null)做爲結果),接下來的一行就是檢查轉型是否有效,及是否是爲空,跟着就顯示Help attribute的「Description」屬性。
class QueryApp
{
publicstaticvoid 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
{
publicstaticvoid 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