C# 自定義特性


特性是一種允許我們向程序集增加元數據的語言結構,它是用於保存程序結構信息的某種特殊類型的類。

根據慣例,特性名使用Pascal命名法並且以Attribute后綴結尾。當為目標應用特性時,我們可以不使用后綴。例如對於SerializableAttributeMyAttributeAttribute這兩個特性,我們在把他們應用到結構時可以使用SerializableMyAttribute短名稱。

所有特性類都派生自System.Attribute,用戶自定義的特殊類叫做自定義特性。

聲明自定義特性

  • 派生自System.Attribute
  • 起一個以后綴為Attribute結尾的名字

為安全起見,建議聲明一個sealed的特性類

  • 由於特性持有目標的信息,所以特性類的公共成員只能是:字段,屬性,構造函數。

使用特性的構造函數

和其他類一樣,都有構造函數,每一個特性至少必須有一個公共構造函數,如果不聲明構造函數,編譯器會為我們產生一個隱式,公共且無參的構造函數,也可以被重載,聲明構造函數時,必須使用類全名(即包括后綴)。在應用時,才可以使用短名稱(不包括后綴)

[MyAttribute("Holds a value")] //使用了一個字符串的構造函數,它只是聲明語句,只有特性的消費者訪問特性時候才能調用構造函數,它不會決定什么時候構造特性類的對象。
public int MyField;

構造函數中的位置參數和命名參數

[MyAttribute("An excellent class",Review="Amy",ver="0.7.1")]

第一個參數是位置參數,后兩個是命名參數。

public sealed class MyAttributeAttribute:System.Attribute
{
    public string Description;
    public string Ver;
    public string Reviewer;
    public MyAttributeAttribute(string desc){
        Description=desc;
    }
}

[MyAttribute("Excellent class",Reviewer="CJ266",Ver="0.7.1")] //雖然構造函數只有一個形參,但我們可以通過命名參數給構造函數3個實參,這與普通的類是不一樣的。
class MyClass{
    
}

上述代碼表示,構造函數的聲明只列出一個形參,但我們可以通過命名參數給構造函數3個,但需要注意的是,構造函數需要的任何位置參數都必須放在命名參數之前。

限制特性

特性本身就是類,有一個很重要的預定義特性可以應用到自定義特性上,那就是AttributeUsage特性,可以用它來限制特性使用在某個目標類型上。

例如,如果我們希望自定義特性MyAttribute只應用到方法上,那么可以以如下方式使用AttributeUsage

[AttributeUsage(AttributeTarget.Method)]
public sealed class MyAttributeAttribute:System.Attribute{...}

AttributeUsage有三個重要的公共屬性:

名字 意義 默認值
ValidOn 限制特性能應用的目標類型的列表,構造函數的第一個參數必須是AttributeTarget類型的枚舉值
Inherited 一個布爾值,指示特性是否會被裝飾類型的派生類所繼承 true
AllowMultiple 一個指示目標是否被應用多個特性的實例的布爾值 false

AttributeTarget的枚舉值成員:

All Assembly Class Constructor
Delegate Enum Event Field
GenericParameter Interface Method Module
Parameter Property ReturnValue Struct

在使用AttributeUsage時,構造函數至少需要一個參數,參數包含的目標類型會保存在ValidOn中,還可以通過命名參數有選擇地設置InheritedAllowMultiple屬性。

訪問特性

我們可以通過Type對象獲取了解有關類型的幾乎所有信息:

成員 成員類型 描述
Name 屬性 返回類型的名字
Namespace 屬性 返回包含類型聲明的命名空間
Assembly 屬性 返回聲明類型的程序集,如果類型是泛型的,返回定義這個類型的程序集
GetFields 方法 返回類型的字段列表
GetProperties 方法 返回類型的屬性列表
GetMethods 方法 返回類型的方法列表

對於訪問自定義特性來說,我們也可以用Type的兩個方法(IsDefinedGetCustomAttributes)

IsDefined方法

使用IsDefined方法來判斷特性是否應用到了,第一個參數是接受需要檢查特性的Type對象,第二個參數是bool類型,指示是否搜索繼承樹來查找這個特性。

GetCustomAttributes方法

該方法返回的對象是object的數組,因此我們必須強制轉換為相應的特性類型,布爾參數指定是否搜索繼承樹來查找特性。

    [AttributeUsage(AttributeTargets.Class)]
    public sealed class ReviewCommentAttribute : System.Attribute
    {
        public string Description { get; set; }
        public string VersionNumber { get; set; }
        public string ReviewerID { get; set; }
        public ReviewCommentAttribute(string desc,string ver)
        {
            Description = desc;
            VersionNumber = ver;
        }
    }


    class BaseClass
    {
        public int BaseField = 0;
    }
    [ReviewComment("This is Derived","0.8.1")]
    class DerivedClass : BaseClass
    {
        public int DerivedField = 0;
    }

class Program1
    {
        static void Main()
        {
            var bc = new BaseClass();
            var dc = new DerivedClass();
            BaseClass[] bca = new BaseClass[] { dc, bc };
            foreach (var v in bca)
            {
                Console.WriteLine("object type:{0}", v.GetType().Name);
                var fi = v.GetType().GetFields();
                Console.WriteLine($"IsDefined:{v.GetType().Name}:{v.GetType().IsDefined(typeof(ReviewCommentAttribute), false)}");
                foreach (var f in fi)
                {
                    Console.WriteLine("Field:{0}", f.Name);

                }
            }

            var t = dc.GetType();
            var Attrs = t.GetCustomAttributes(true);
            foreach (var attr in Attrs)
            {
                var attr1 = attr as ReviewCommentAttribute;
                Console.WriteLine($"{attr1.Description}");
            }
           
        }
    }

image-20210904232837738


免責聲明!

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



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