有關C#標簽Attribute的熟悉


Attribute 簡單用法:

  最近用到了,所以靜下心來找些資料看了一下,終於把這東西搞清楚了。

一.什么是Attribute

先看下面的三段代碼:

1.自定義Attribute類:VersionAttribute

 

 [AttributeUsage(AttributeTargets.Class)]
    public class VersionAttribute : Attribute
    {
        public string Name { get; set; }
        public string Date { get; set; }
        public string Describtion { get; set; }
    }
2.使用自定義Attribute的Class:

    [Version(Name = "hyddd", Date = "2009-07-20", Describtion = "hyddd's class")]
    public class MyCode
    {
        //...
    }
3.上面這個Class中的Attribute一般會被如何使用呢?

    class Program
    {
        static void Main(string[] args)
        {
            var info = typeof(MyCode);
            var classAttribute = (VersionAttribute)Attribute.GetCustomAttribute(info, typeof(VersionAttribute));
            Console.WriteLine(classAttribute.Name);
            Console.WriteLine(classAttribute.Date);
            Console.WriteLine(classAttribute.Describtion);
        }
    }

示例完畢!上面三段代碼相信已經說明了Attribute大概是一個什么東西和怎么去用。

 

二.深入討論Attribute

1.Attribute的概念定義

     關於Attribute概念的定義,我直接引用《你必須知道的.NET之特性和屬性》中的一段來說明:

     MADN的定義為:公共語言運行時允許添加類似關鍵字的描述聲明,叫做attributes, 它對程序中的元素進行標注,如類型、字段、方法和屬性等。Attributes和Microsoft .NET Framework文件的元數據(metadata)保存在一起,可以用來向運行時描述你的代碼,或者在程序運行的時候影響應用程序的行為。

     我們簡單的總結為:定制特性attribute,本質上是一個類,其為目標元素提供關聯附加信息,並在運行期以反射的方式來獲取附加信息。

噢,原來Attribute的目的是為元素提供關聯附加信息。其中,上面第一段代碼中“[AttributeUsage(AttributeTargets.Class)] ”說明了Attribute提供附加信息的元素是Class,所以如果上面第二段的代碼改為:

 

public class MyCode
{
    [Version(Name = "hyddd", Date = "2009-07-20", Describtion = "hyddd's class")]
    public void Test() { }
}

會出現編譯錯誤。

2.Attribute作為編譯指令

     Attribute類是在編譯的時候被實例化的,而不是像通常的類那樣在運行時候才實例化。所以在第三段代碼中,你可以在沒有實例化MyCode對象的情況下取到MyCode的Attribute信息。由於Attribute類是在編譯的時候被實例化的,所以你還可以用外部工具維護這些Attribute信息。

3.Attribute與Property

     從中文來說,Attribute和Property的中文都叫“屬性”,很容易讓人混淆。現在的文章,Attribute一般翻譯為”特性”,而Property稱為“屬性”。

     或許你會問,我用靜態的Property/Field一樣可以做到在不實例化的時候拿到一些信息,如果這樣的話,Attribute又有什么存在意義呢?

1.Property:

     Property可以說是一個面向對象的概念,提供了對私有字段的訪問封裝,在C#中以get和set訪問器方法實現對可讀可寫屬性的操作,提供了安全和靈活的數據訪問封裝。比如:

 

public class Robot
    {
        private string name = "";   //字段:Field
        public string Name          //屬性:Property,對Field進行封裝。
          {
            get { return name; }
            set { name = value; }
        }
    }

2.Attribute:

     Attribute的目標是:為元素提供附加信息。它的作用更類似於注釋。

可以說,Property/Field和Attribute是兩個完全不同的概念,雖然他們有些時候能做一樣的事,但請記住,他們是從本質上就不同的兩個東西。

三.實現自己的Attribute時需要注意的一些問題

1.自定義的Attribute必須直接或者間接繼承System.Attribute。

2.這里有一個約定:所有自定義的特性名稱都應該有個Attribute后綴。因為當你的Attribute施加到一個程序的元素上的時候,編譯器先查找你的Attribute的定義,如果沒有找到,那么它就會查找“Attribute名稱"+Attribute的定義。如果都沒有找到,那么編譯器就報錯。這就是為什么我可以再上面第一段代碼中,定義一個VersionAttribute,但在第二段代碼中,我使用卻是Version這個Attribute。:>

下面是一些開發自定義Attribute時,可能需要用到的資料:

【1】Attribute可以關聯的元素包括:

      程序集(assembly)、模塊(module)、類型(type)、屬性(property)、事件(event)、字段(field)、方法(method)、參數(param)、返回值(return)。例如

[assembly: Version(Name = "hyddd", Date = "2009-07-20", Describtion = "hyddd's class")]
    public class MyCode
    {
        //......
    }

 

 

Attribute 進階用法:

1、什么是Atrribute 
首先,我們肯定Attribute是一個類,下面是msdn文檔對它的描述:
公共語言運行時允許你添加類似關鍵字的描述聲明,叫做attributes, 它對程序中的元素進行標注,如類型、字段、方法和屬性等。Attributes和Microsoft .NET Framework文件的元數據保存在一起,可以用來向運行時描述你的代碼,或者在程序運行的時候影響應用程序的行為。

在.NET中,Attribute被用來處理多種問題,比如序列化、程序的安全特征、防止即時編譯器對程序代碼進行優化從而代碼容易調試等等。下面,我們先來看幾個在.NET中標准的屬性的使用,稍后我們再回過頭來討論Attribute這個類本身。(文中的代碼使用C#編寫,但同樣適用所有基於.NET的所有語言)

2、Attribute作為編譯器的指令
在C#中存在着一定數量的編譯器指令,如:#define DEBUG, #undefine DEBUG, #if等。這些指令專屬於C#,而且在數量上是固定的。而Attribute用作編譯器指令則不受數量限制。比如下面的三個Attribute:

   Conditional:起條件編譯的作用,只有滿足條件,才允許編譯器對它的代碼進行編譯。一般在程序調試的時候使用。 
   DllImport:用來標記非.NET的函數,表明該方法在一個外部的DLL中定義。 
   Obsolete:這個屬性用來標記當前的方法已經被廢棄,不再使用了。
下面的代碼演示了上述三個屬性的使用:

 

1#define DEBUG //這里定義條件
   
 3using System;
 4using System.Runtime.InteropServices;
 5using System.Diagnostics;
   
 7namespace AttributeDemo
 8{
  class MainProgramClass
  {

     [DllImport("User32.dll")]
     public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
    
     static void Main(string[] args)
     {
        DisplayRunningMessage();
        DisplayDebugMessage();
    
        MessageBox(0,"Hello","Message",0);
    
        Console.ReadLine();
     }
    
     [Conditional("DEBUG")]
     private static void DisplayRunningMessage()
     {
        Console.WriteLine("開始運行Main子程序。當前時間是"+DateTime.Now);
     }
 
     [Conditional("DEBUG")]
     //[Obsolete("Don't use Old method, use New method", true)] 
     [Obsolete]
     private static void DisplayDebugMessage()
     {
        Console.WriteLine("開始Main子程序");
     }
  }
39}

如果在一個程序元素前面聲明一個Attribute,那么就表示這個Attribute被施加到該元素上,前面的代碼,[DllImport]施加到MessageBox函數上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。

根據上面涉及到的三個Attribute的說明,我們可以猜到程序運行的時候產生的輸出:DllImport Attribute表明了MessageBox是User32.DLL中的函數,這樣我們就可以像內部方法一樣調用這個函數。

重要的一點就是Attribute就是一個類,所以DllImport也是一個類,Attribute類是在編譯的時候被實例化的,而不是像通常的類那樣在運行時候才實例化。Attribute實例化的時候根據該Attribute類的設計可以帶參數,也可以不帶參數,比如DllImport就帶有"User32.dll"的參數。Conditional對滿足參數的定義條件的代碼進行編譯,如果沒有定義DEBUG,那么該方法將不被編譯,讀者可以把#define DEBUG一行注釋掉看看輸出的結果(release版本,在Debug版本中Conditional的debug總是成立的)。Obsolete表明了DispalyDebugMessage方法已經過時了,它有一個更好的方法來代替它,當我們的程序調用一個聲明了Obsolete的方法時,那么編譯器會給出信息,Obsolete還有其他兩個重載的版本。大家可以參考msdn中關於的ObsoleteAttribute 類的描述。

3、Attribute類
除了.NET提供的那些Attribute派生類之外,我們可以自定義我們自己的Attribute,所有自定義的Attribute必須從Attribute類派生。現在我們來看一下Attribute 類的細節:

protected Attribute(): 保護的構造器,只能被Attribute的派生類調用。

三個靜態方法:

static Attribute GetCustomAttribute():這個方法有8種重載的版本,它被用來取出施加在類成員上指定類型的Attribute。

static Attribute[] GetCustomAttributes(): 這個方法有16種重載版本,用來取出施加在類成員上指定類型的Attribute數組。

static bool IsDefined():由八種重載版本,看是否指定類型的定制attribute被施加到類的成員上面。

實例方法:

bool IsDefaultAttribute(): 如果Attribute的值是默認的值,那么返回true。

bool Match():表明這個Attribute實例是否等於一個指定的對象。

公共屬性: TypeId: 得到一個唯一的標識,這個標識被用來區分同一個Attribute的不同實例。

我們簡單地介紹了Attribute類的方法和屬性,還有一些是從object繼承來的。這里就不列出來了。

下面介紹如何自定義一個Attribute: 自定義一個Attribute並不需要特別的知識,其實就和編寫一個類差不多。自定義的Attribute必須直接或者間接地從Attribute這個類派生,如:

public MyCustomAttribute : Attribute { ... }

這里需要指出的是Attribute的命名規范,也就是你的Attribute的類名+"Attribute",當你的Attribute施加到一個程序的元素上的時候,編譯器先查找你的Attribute的定義,如果沒有找到,那么它就會查找“Attribute名稱"+Attribute的定義。如果都沒有找到,那么編譯器就報錯。

 

4、定義或控制特性的使用

對於一個自定義的Attribute,你可以通過AttributeUsage的Attribute來限定你的Attribute 所施加的元素的類型。代碼形式如下:
[AttriubteUsage(參數設置)] public 自定義Attribute : Attribute { ... }

 非常有意思的是,AttributeUsage本身也是一個Attribute,這是專門施加在Attribute類的Attribute. AttributeUsage自然也是從Attribute派生,它有一個帶參數的構造器,這個參數是AttributeTargets的枚舉類型。下面是AttributeTargets 的定義:

 

1public enum AttributeTargets
 2{
  All=16383,
  Assembly=1,
  Module=2,
  Class=4,
  Struct=8,
  Enum=16,
  Constructor=32,
  Method=64,
  Property=128,
  Field=256,
  Event=512,
  Interface=1024,
  Parameter=2048,
  Delegate=4096,
  ReturnValue=8192
18}

作為參數的AttributeTarges的值允許通過“或”操作來進行多個值得組合,如果你沒有指定參數,那么默認參數就是All 。 AttributeUsage除了繼承Attribute 的方法和屬性之外,還定義了以下三個屬性:

AllowMultiple: 讀取或者設置這個屬性,表示是否可以對一個程序元素施加多個Attribute 。

Inherited:讀取或者設置這個屬性,表示是否施加的Attribute 可以被派生類繼承或者重載。

ValidOn: 讀取或者設置這個屬性,指明Attribute 可以被施加的元素的類型。

下面讓我們來做一些實際的東西。我們將會在Help特性前放置AttributeUsage特性以期待在它的幫助下控制Help特性的使用。

 

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特性只能被放在class的前面。這也就意味着下面的代碼將會產生錯誤:

 

[Help("this is a do-nothing class")] 
public class AnyClass 
{ 
[Help("this is a do-nothing method")] //error 
public void AnyMethod() 
{ 
} 
}

編譯器報告錯誤如下:

  AnyClass.cs: Attribute 'Help' is not valid on this declaration type.

  It is valid on 'class' declarations only.

 

  我們可以使用AttributeTargets.All來允許Help特性被放置在任何程序實體前。可能的值是:

  Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,
Parameter,Delegate。

  All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate,

  ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface )

  下面考慮一下AllowMultiple = false。它規定了特性不能被重復放置多次。

 

[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, 表明當特性被放置在一個基類上時,它能否被派生類所繼承。

 

[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 ]

第一種情況:

  如果我們查詢(Query)(稍后我們會看到如何在運行期查詢一個類的特性)Derive類,我們將會發現Help特性並不存在,因為inherited屬性被設置為false。

  第二種情況:

  和第一種情況相同,因為inherited也被設置為false。

  第三種情況:

  為了解釋第三種和第四種情況,我們先來給派生類添加點代碼:

 

[Help("BaseClass")] 
public class Base 
{ 
} 
[Help("DeriveClass")] 
public class Derive : Base 
{ 
}

現在我們來查詢一下Help特性,我們只能得到派生類的屬性,因為inherited被設置為true,但是AllowMultiple卻被設置為false。因此基類的Help特性被派生類Help特性覆蓋了。

  第四種情況:

  在這里,我們將會發現派生類既有基類的Help特性,也有自己的Help特性,因為AllowMultiple被設置為true。

 

至此,我們介紹了有關Attribute類和它們的代碼格式。你一定想知道到底如何在你的應用程序中使用Attribute,如果僅僅是前面介紹的內容,還是不足以說明Attribute有什么實用價值的話,那么從后面的章節開始我們將介紹幾個Attribute的不同用法,相信你一定會對Attribute有一個新的了解。

 

參考文章:

http://www.cnblogs.com/hyddd/archive/2009/07/20/1526777.html

http://www.cnblogs.com/luckboy/archive/2009/07/18/1526083.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM