C#中的方括號[](特性,屬性)

轉自:http://www.txwb.com/Article/wbcx/Easy/201104/94502.html

原 文(http://www.codeproject.com/Articles/2933/Attributes-in-C)

約定:

     1.”attribute”  ”attributes” 均不翻譯

     2.”property” 譯爲“屬性”

     3.msdn 中的原句不翻譯

     4.”program entity” 譯爲  語言元素 

Attributes in C#

介紹

Attributes 是一種新的描述信息,我們既可以使用 attributes 來定義設計期信息(例如 幫助文件,文檔的 URL ),還可以用attributes 定義運行時信息(例如,使 XML 中的元素與類的成員字段關聯起來)。我們也可以用 attributes 來創建一個“自描述”的組件。在這篇指南中我們將明白怎麼創建屬性並將其綁定至各種語言元素上,另外我們怎樣在運行時環境下獲取到 attributes 的一些信息。

定義

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. 

using  System; 

public   class  AnyClass  

{ 
    [Obsolete( " Don't use Old method, use New method " ,  true )] 

    static   void  Old( ) { } 

    static   void  New( ) { } 

    public   static   void  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; 

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 可以放置在任何預定義的語言元素上,那些可能的語言元素如下 :  

  • 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 " )] 
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 
{ 
} 

我們有四種可能的綁定 

  • [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 " )]  
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 類的參數類型被限定在如下類型中:

  • 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 中所有已定義的類,類型,還有方法。

記住那舊的 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} " , elpAttr.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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章