C# 特性(Attribute)


Attribute是什么

Attribute是一種可由用戶自有定義的修飾符(Modifier),可以用來修飾各種需要被修飾的目標。我們可以對類、以及C#程序集中的成員進行進一步的描述。

簡單地說,Attribute就是一種“附着物”——就像牡蠣吸附在船底或礁石上一樣。 這些附着物的作用是為它們的附着體追加上一些額外的信息(這些信息保存在附着物的體內)——比如“這個類是我寫的”或者“這個函數以前出過問題”等等

Attribute與注釋的區別

注釋是對程序源代碼的一種說明,主要目的是給人看的,在程序被編譯的時候會被編譯器所丟棄,因此,它絲毫不會影響到程序的執行。

Attribute是程序代碼的一部分,它不但不會被編譯器丟棄,而且還會被編譯器編譯進程序集(Assembly)的元數據(Metadata)里。在程序運行的時候,隨時可以從元數據(元數據:.NET中元數據是指程序集中的命名空間、類、方法、屬性等信息,這些信息是可以通過Reflection讀取出來的。)中提取提取出這些附加信息,並以之決策程序的運行。

示例

比如我們寫一個關於人的類Person,該類可以對人的屬性以及某些行為(方法)進行描述。那么如果我們要對人類進行進一步描述呢?比如人這個類是屬於動物的靈長類動物,有人會說我們可以為這個Person類去寫一個靈長動物類的父類,再用人類去繼承這個類去解決。但是我們要求的是僅僅是描述性的,就是對這個人類進行進一步的描述,而在實際操作中不需要去操作。這樣的情況我們就可以用特性的概念去解決,特性簡而言之就是程序集的特定程序元素所具有的另外的性質。

//先定義一個人類Person類
        class Person { //人的姓名儲存字段和屬性 
            private string name; public string Name { set { name = value; } get { return name; } } //人的年齡儲存字段和屬性 
            private int age; public int Age { set { age = value; } get { return age; } } //人的性別儲存字段和屬性 
            private char sex; public char Sex { set { sex = value; } get { return sex; } } //人的打招呼方法 
            public void SayHello() { Console.WriteLine($"大家好,我叫{this.Name},我今年{this.Age}歲了,我的性別是{this.Sex}"); } } //定義動物的特性類AnimalAttribute類繼承於Attribute(特性)
        class AnimalAttribute : Attribute { //字段和屬性描述是否是靈長類
            private bool isPrimate; public bool IsPrimate { set { isPrimate = value; } get { return isPrimate; } } }
View Code

對人類進行動物類描述。即在人類的定義前面加:

[Animal(IsPrimate = true)]  //為人類加特性,指定人類是靈長類。

 下面我們就可以通過代碼來獲得人類的特性:

//聲明特性對象,並通過Attribute類的靜態方法GetCustomAttribute()獲得人類的在動物類的特性,並賦值給特性對象 
            Attribute att1 = Attribute.GetCustomAttribute(typeof(Person), typeof(AnimalAttribute)); //將特性對象轉化為動物特性對象
            AnimalAttribute animalAtt = att1 as AnimalAttribute; //檢查轉化是否成功如果成功則打印這個特性對象的是否是靈長類的屬性。
            if (animalAtt != null) { Console.WriteLine("人類是否是靈長類:{0}", animalAtt.IsPrimate); } Console.ReadKey(); 
View Code

注意:如果一個類有的某個特性類描述多次,則要用GetCustomAttributes()方法。

Attribute的使用方法:(四種方式完全等價)

//長記法
[ConditionalAttribute("Li")] [ConditionalAttribute("NoBug")] public static void Func() {Console.WriteLine("Created by Li, NoBug"); } //短記法
[Conditional("Li")] [Conditional("NoBug")] public static void Func() {Console.WriteLine("Created by Li, NoBug"); } //換序
[Conditional("NoBug")] [Conditional("Li")] public static void Func() {Console.WriteLine("Created by Li, NoBug"); } //單括號疊加
[Conditional("NoBug"),Conditional("Li")] public static void Func() {Console.WriteLine("Created by Li, NoBug"); } 

預定義的特性

obsolete類用來指定該類的成員已經過時,在程序使用這個成員的時候會給出提示。

obsolete類有三種重載

  public ObsoleteAttribute()

  public ObsoleteAttribute(string message)  參數說明: message:描述了可選的變通方法文本字符串。

  public ObsoleteAttribute(string message, bool error)  參數說明:message:描述了可選的變通方法文本字符串。  error:true 如果使用過時的元素將生成編譯器錯誤;false 如果使用它將生成編譯器警告。

比如如果我們的Person類的SayHello()方法現在不能用了,不允許程序員使用這個方法,那么我們就可以在Person類的SayHello方法前面加[Obsolete("該方法已經過期,請找最新的方法", true)]

這樣當我們在主程序中用到Person類的SayHello方法的時候程序就會報錯。當然,如果第二個參數設置為false時,在調用該成員的時候不會報錯,但是會報出警告。

Attribute作為編譯器的指令

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

   Conditional:起條件編譯的作用,只有滿足條件,才允許編譯器對它的代碼進行編譯。一般在程序調試的時候使用。 

   DllImport:用來標記非.NET的函數,表明該方法在一個外部的DLL中定義。 

   Obsolete:這個屬性用來標記當前的方法已經被廢棄,不再使用了。

下面的代碼演示了上述三個屬性的使用:

#define debug   //定義條件
using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace ConsoleApp1 { class Program { [DllImport("User32.dll")] public static extern int MessageBox(int hParent, string Message, string Caption, int Type); [Conditional("debug")] private static void DisplayRunningMessage() { Console.WriteLine($"開始運行Main子程序。當前時間是{DateTime.Now}"); } [Conditional("debug")] [Obsolete] private static void DisplayDebugMessage() { Console.WriteLine("開始Main子程序"); } static void Main(string[] args) { DisplayRunningMessage(); DisplayDebugMessage(); MessageBox(0, "Hello", "Message", 0); Console.ReadKey(); } } }

運行結果:

解析:

如果在一個程序元素前面聲明一個Attribute,那么就表示這個Attribute被施加到該元素上。

上面的代碼,[DllImport]施加到MessageBox函數上, [Conditional]施加到DisplayRuntimeMessage方法和DisplayDebugMessage方法,[Obsolete]施加到DisplayDebugMessage方法上。DllImport Attribute表明了MessageBox是User32.DLL中的函數,這樣我們就可以像內部方法一樣調用這個函數。

重要的一點就是Attribute就是一個類,所以DllImport也是一個類,Attribute類是在編譯的時候被實例化的,而不是像通常的類那樣在運行時候才實例化。Attribute實例化的時候根據該Attribute類的設計可以帶參數,也可以不帶參數,比如DllImport就帶有"User32.dll"的參數。Conditional對滿足參數的定義條件的代碼進行編譯,如果沒有定義debug,那么該方法將不被編譯。Obsolete表明了DispalyDebugMessage方法已經過時了,它有一個更好的方法來代替它,當我們的程序調用一個聲明了Obsolete的方法時,那么編譯器會給出信息。

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:

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

public MyCustomAttribute : Attribute { ... }

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

自定義或控制特性的使用

對於一個自定義的Attribute,可以通過AttributeUsage的Attribute來限定你的Attribute所施加的元素的類型。代碼形式如下:

[AttriubteUsage(參數設置)] public 自定義Attribute : Attribute { ... }

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

//
    // 摘要: // 指定可應用屬性的應用程序元素。
    [ComVisible(true)] [Flags] public enum AttributeTargets { //
        // 摘要: // 特性可以應用於程序集。
        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, //
        // 摘要: // 特性可以應用於泛型參數。
        GenericParameter = 16384, //
        // 摘要: // 特性可以應用於任何應用程序元素。
        All = 32767 }
View Code

作為參數的AttributeTarges的值允許通過“或”操作來進行多個值得組合,如果你沒有指定參數,那么默認參數就是All 。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)]

AttributeUsage除了繼承Attribute 的方法和屬性之外,還定義了以下三個屬性:

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

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

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

示例:在Help特性前放置AttributeUsage特性以期待在它的幫助下控制Help特性的使用

[AttributeUsage(AttributeTargets.All, 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的前面。這也就意味着下面的代碼將會產生錯誤:

我們可以使用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。它規定了特性不能被重復放置多次

 

 

 

 

原文鏈接:https://blog.csdn.net/qq_38507850/article/details/79181319

原文鏈接:https://blog.csdn.net/xiaouncle/article/details/70216951

原文鏈接:https://www.cnblogs.com/wangluochong/p/3733897.html


免責聲明!

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



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