中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C#自定義特性的示例分析

發布時間:2021-12-01 10:54:52 來源:億速云 閱讀:187 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關C#自定義特性的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

C#自定義特性范例介紹

如果不能自己定義一個特性并使用它,我想你怎么也不能很好的理解特性,我們現在就自己構建一個特性。假設我們有這樣一個很常見的需求:我們在創建或者更新一個類文件時,需要說明這個類是什么時候、由誰創建的,在以后的更新中還要說明在什么時候由誰更新的,可以記錄也可以不記錄更新的內容,以往你會怎么做呢?是不是像這樣在類的上面給類添加注釋:

//更新:Matthew, 2008-2-10, 修改 ToString()方法  //更新:Jimmy, 2008-1-18  //  public class DemoClass{      // Class Body  }

這樣的的確確是可以記錄下來,但是如果有一天我們想將這些記錄保存到數據庫中作以備份呢?你是不是要一個一個地去查看源文件,找出這些注釋,再一條條插入數據庫中呢?

通過上面特性的定義,我們知道特性可以用于給類型添加元數據(描述數據的數據,包括數據是否被修改、何時創建、創建人,這些數據可以是一個類、方法、屬性),這些元數據可以用于描述類型。那么在此處,特性應該會派上用場。那么在本例中,元數據應該是:注釋類型(“更新”或者“創建”),修改人,日期,備注信息(可有可無)。而特性的目標類型是DemoClass類。

按照對于附加到DemoClass類上的元數據的理解,我們先創建一個封裝了元數據的類RecordAttribute:

public class RecordAttribute {         private string recordType;      // 記錄類型:更新/創建         private string author;          // 作者         private DateTime date;          // 更新/創建 日期         private string memo;         // 備注             // 構造函數,構造函數的參數在特性中也稱為“位置參數”。         public RecordAttribute(string recordType, string author, string date) {            this.recordType = recordType;            this.author = author;            this.date = Convert.ToDateTime(date);         }             // 對于位置參數,通常只提供get訪問器         public string RecordType {   get { return recordType; }   }         public string Author { get { return author; } }         public DateTime Date { get { return date; } }             // 構建一個屬性,在特性中也叫“命名參數”         public string Memo {            get { return memo; }            set { memo = value; }         }     }

NOTE:注意構造函數的參數 date,必須為一個常量、Type類型、或者是常量數組,所以不能直接傳遞DateTime類型。

這個類不光看上去,實際上也和普通的類沒有任何區別,顯然不能它因為名字后面跟了個Attribute就搖身一變成了特性。那么怎樣才能讓它稱為特性并應用到一個類上面呢?進行下一步之前,我們看看.Net內置的特性Obsolete是如何定義的:

namespace System {      [Serializable]      [AttributeUsage(6140, Inherited = false)]      [ComVisible(true)]      public sealed class ObsoleteAttribute : Attribute {          public ObsoleteAttribute();         public ObsoleteAttribute(string message);         public ObsoleteAttribute(string message, bool error);          public bool IsError { get; }         public string Message { get; }      }  }

添加特性的格式(位置參數和命名參數)

首先,我們應該發現,它繼承自Attribute類,這說明我們的 RecordAttribute 也應該繼承自Attribute類。 (一個特性類與普通類的區別是:繼承了Attribute類)

其次,我們發現在這個特性的定義上,又用了三個特性去描述它。這三個特性分別是:Serializable、AttributeUsage 和 ComVisible。Serializable特性我們前面已經講述過,ComVisible簡單來說是“控制程序集中個別托管類型、成員或所有類型對 COM 的可訪問性”(微軟給的定義)。這里我們應該注意到:特性本身就是用來描述數據的元數據,而這三個特性又用來描述特性,所以它們可以認為是“元數據的元數據”(元元數據:meta-metadata)。

(從這里我們可以看出,特性類本身也可以用除自身以外的其它特性來描述,所以這個特性類的特性是元元數據。)

因為我們需要使用“元元數據”去描述我們定義的特性 RecordAttribute,所以現在我們需要首先了解一下“元元數據”。這里應該記得“元元數據”也是一個特性,大多數情況下,我們只需要掌握 AttributeUsage就可以了,所以現在就研究一下它。我們首先看上面AttributeUsage是如何加載到ObsoleteAttribute特性上面的。

[AttributeUsage(6140, Inherited = false)]

然后我們看一下AttributeUsage的定義:

namespace System {      public sealed class AttributeUsageAttribute : Attribute {         public AttributeUsageAttribute(AttributeTargets validOn);          public bool AllowMultiple { get; set; }         public bool Inherited { get; set; }         public AttributeTargets ValidOn { get; }      }  }

可以看到,它有一個構造函數,這個構造函數含有一個AttributeTargets類型的位置參數(Positional Parameter) validOn,還有兩個命名參數(Named Parameter)。注意ValidOn屬性不是一個命名參數,因為它不包含set訪問器,(是位置參數)。

這里大家一定疑惑為什么會這樣劃分參數,這和特性的使用是相關的。假如AttributeUsageAttribute 是一個普通的類,我們一定是這樣使用的:

// 實例化一個 AttributeUsageAttribute 類

AttributeUsageAttribute usage=new AttributeUsageAttribute(AttributeTargets.Class);

usage.AllowMultiple = true;  // 設置AllowMutiple屬性

usage.Inherited = false;// 設置Inherited屬性

但是,特性只寫成一行代碼,然后緊靠其所應用的類型(目標類型),那么怎么辦呢?微軟的軟件工程師們就想到了這樣的辦法:不管是構造函數的參數 還是 屬性,統統寫到構造函數的圓括號中,對于構造函數的參數,必須按照構造函數參數的順序和類型;對于屬性,采用“屬性=值”這樣的格式,它們之間用逗號分隔。于是上面的代碼就減縮成了這樣:

[AttributeUsage(AttributeTargets.Class, AllowMutiple=true, Inherited=false)]

可以看出,AttributeTargets.Class是構造函數參數(位置參數),而AllowMutiple 和 Inherited實際上是屬性(命名參數)。命名參數是可選的。將來我們的RecordAttribute的使用方式于此相同。(為什么管他們叫參數,我猜想是因為它們的使用方式看上去更像是方法的參數吧。)

假設現在我們的RecordAttribute已經OK了,則它的使用應該是這樣的:

C#代碼

[RecordAttribute("創建","張子陽","2008-1-15",Memo="這個類僅供演示")]     public class DemoClass{         // ClassBody     }

其中recordType, author 和 date 是位置參數,Memo是命名參數。

C#自定義特性:AttributeTargets 位標記

從AttributeUsage特性的名稱上就可以看出它用于描述特性的使用方式。具體來說,首先應該是其所標記的特性可以應用于哪些類型或者對象。從上面的代碼,我們看到AttributeUsage特性的構造函數接受一個 AttributeTargets 類型的參數,那么我們現在就來了解一下AttributeTargets。

AttributeTargets 是一個位標記,它定義了特性可以應用的類型和對象。

[Flags]

public enum AttributeTargets {

Assembly = 1,         //可以對程序集應用屬性。

Module = 2,              //可以對模塊應用屬性。

Class = 4,            //可以對類應用屬性。

Struct = 8,              //可以對結構應用屬性,即值類型。

Enum = 16,            //可以對枚舉應用屬性。

Constructor = 32,     //可以對構造函數應用屬性。

Method = 64,          //可以對方法應用屬性。

Property = 128,           //可以對屬性 (Property) 應用屬性 (Attribute)。

Field = 256,          //可以對字段應用屬性。

Event = 512,          //可以對事件應用屬性。

Interface = 1024,            //可以對接口應用屬性。

Parameter = 2048,            //可以對參數應用屬性。

Delegate = 4096,             //可以對委托應用屬性。

ReturnValue = 8192,             //可以對返回值應用屬性。

GenericParameter = 16384,    //可以對泛型參數應用屬性。

All = 32767,  //可以對任何應用程序元素應用屬性。

}

現在應該不難理解為什么上面我范例中用的是:

[AttributeUsage(AttributeTargets.Class, AllowMutiple=true, Inherited=false)]

而ObsoleteAttribute特性上加載的 AttributeUsage是這樣的:

[AttributeUsage(6140, Inherited = false)]

因為AttributeUsage是一個位標記,所以可以使用按位或“|”來進行組合。所以,當我們這樣寫時:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)

意味著既可以將特性應用到類上,也可以應用到接口上。

NOTE:這里存在著兩個特例:觀察上面AttributeUsage的定義,說明特性還可以加載到程序集Assembly和模塊Module上,而這兩個屬于我們的編譯結果,在程序中并不存在這樣的類型,我們該如何加載呢?可以使用這樣的語法:[assembly:SomeAttribute(parameter list)],另外這條語句必須位于程序語句開始之前。

C#自定義特性:Inherited 和 AllowMutiple屬性

AllowMutiple 屬性用于設置該特性是不是可以重復地添加到一個類型上(默認為false),就好像這樣:

[RecordAttribute("更新","Jimmy","2008-1-20")]

[RecordAttribute("創建","張子陽","2008-1-15",Memo="這個類僅供演示")]

public class DemoClass{

// ClassBody

}

所以,我們必須顯示的將AllowMutiple設置為True。

Inherited 就更復雜一些了,假如有一個類繼承自我們的DemoClass,那么當我們將RecordAttribute添加到DemoClass上時,DemoClass的子類也會獲得該特性。而當特性應用于一個方法,如果繼承自該類的子類將這個方法覆蓋,那么Inherited則用于說明是否子類方法是否繼承這個特性。

在我們的例子中,將 Inherited 設為false。

C#自定義特性:實現 RecordAttribute

現在實現RecordAttribute應該是非常容易了,對于類的主體不需要做任何的修改,我們只需要讓它繼承自Attribute基類,同時使用AttributeUsage特性標記一下它就可以了(假定我們希望可以對類和方法應用此特性):

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple=true, Inherited=false)]

public class RecordAttribute:Attribute {

// 略

}

C#自定義特性:使用 RecordAttribute

我們已經創建好了自己的自定義特性,現在是時候使用它了。

C#代碼

[Record("更新", "Matthew", "2008-1-20", Memo = "修改 ToString()方法")]     [Record("更新", "Jimmy", "2008-1-18")]     [Record("創建", "張子陽", "2008-1-15")]     public class DemoClass {              public override string ToString() {            return "This is a demo class";         }     }         class Program {         static void Main(string[] args) {            DemoClass demo = new DemoClass();            Console.WriteLine(demo.ToString());         }     }

這段程序簡單地在屏幕上輸出一個“This is a demo class”。我們的屬性也好像使用“//”來注釋一樣對程序沒有任何影響,實際上,我們添加的數據已經作為元數據添加到了程序集中。可以通過IL DASM看到:

C#自定義特性的示例分析

感謝各位的閱讀!關于“C#自定義特性的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

瓮安县| 阳信县| 尉犁县| 克拉玛依市| 锦屏县| 平原县| 屏东市| 灵山县| 乾安县| 西林县| 措美县| 额尔古纳市| 贞丰县| 北碚区| 遵义县| 民和| 平顶山市| 肇东市| 浦城县| 婺源县| 潍坊市| 胶南市| 鄂伦春自治旗| 东安县| 奉节县| 赤壁市| 鄄城县| 内丘县| 健康| 苍梧县| 江阴市| 秦安县| 梁平县| 曲周县| 德兴市| 景洪市| 灌云县| 慈利县| 布尔津县| 辽阳市| 南汇区|