自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C#自定義特性介紹

開發(fā) 后端
本文通過實(shí)例介紹了C#自定義特性。如果不能自己定義一個(gè)特性并使用它,我想你怎么也不能很好的理解特性。

C#自定義特性范例介紹

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

  1. //更新:Matthew, 2008-2-10, 修改 ToString()方法  
  2. //更新:Jimmy, 2008-1-18  
  3. //創(chuàng)建:張子陽(yáng), 2008-1-15  
  4. public class DemoClass{  
  5.     // Class Body  

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

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

按照對(duì)于附加到DemoClass類上的元數(shù)據(jù)的理解,我們先創(chuàng)建一個(gè)封裝了元數(shù)據(jù)的類RecordAttribute:

  1. public class RecordAttribute {     
  2.     private string recordType;      // 記錄類型:更新/創(chuàng)建     
  3.     private string author;          // 作者     
  4.     private DateTime date;          // 更新/創(chuàng)建 日期     
  5.     private string memo;         // 備注     
  6.     
  7.     // 構(gòu)造函數(shù),構(gòu)造函數(shù)的參數(shù)在特性中也稱為“位置參數(shù)”。     
  8.     public RecordAttribute(string recordType, string author, string date) {     
  9.        this.recordType = recordType;     
  10.        this.author = author;     
  11.        this.date = Convert.ToDateTime(date);     
  12.     }     
  13.     
  14.     // 對(duì)于位置參數(shù),通常只提供get訪問器     
  15.     public string RecordType {   get { return recordType; }   }     
  16.     public string Author { get { return author; } }     
  17.     public DateTime Date { get { return date; } }     
  18.     
  19.     // 構(gòu)建一個(gè)屬性,在特性中也叫“命名參數(shù)”     
  20.     public string Memo {     
  21.        get { return memo; }     
  22.        set { memo = value; }     
  23.     }     
  24. }    
  25.  

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

這個(gè)類不光看上去,實(shí)際上也和普通的類沒有任何區(qū)別,顯然不能它因?yàn)槊趾竺娓藗€(gè)Attribute就搖身一變成了特性。那么怎樣才能讓它稱為特性并應(yīng)用到一個(gè)類上面呢?進(jìn)行下一步之前,我們看看.Net內(nèi)置的特性O(shè)bsolete是如何定義的:

  1. namespace System {  
  2.     [Serializable]  
  3.     [AttributeUsage(6140, Inherited = false)]  
  4.     [ComVisible(true)]  
  5.     public sealed class ObsoleteAttribute : Attribute {  
  6.  
  7.        public ObsoleteAttribute();  
  8.        public ObsoleteAttribute(string message);  
  9.        public ObsoleteAttribute(string message, bool error);  
  10.  
  11.        public bool IsError { get; }  
  12.        public string Message { get; }  
  13.     }  
  14. }   

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

首先,我們應(yīng)該發(fā)現(xiàn),它繼承自Attribute類,這說明我們的 RecordAttribute 也應(yīng)該繼承自Attribute類。 (一個(gè)特性類與普通類的區(qū)別是:繼承了Attribute類)

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

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

因?yàn)槲覀冃枰褂谩霸獢?shù)據(jù)”去描述我們定義的特性 RecordAttribute,所以現(xiàn)在我們需要首先了解一下“元元數(shù)據(jù)”。這里應(yīng)該記得“元元數(shù)據(jù)”也是一個(gè)特性,大多數(shù)情況下,我們只需要掌握 AttributeUsage就可以了,所以現(xiàn)在就研究一下它。我們首先看上面AttributeUsage是如何加載到ObsoleteAttribute特性上面的。

    [AttributeUsage(6140, Inherited = false)]

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

  1. namespace System {  
  2.     public sealed class AttributeUsageAttribute : Attribute {  
  3.        public AttributeUsageAttribute(AttributeTargets validOn);  
  4.  
  5.        public bool AllowMultiple { getset; }  
  6.        public bool Inherited { getset; }  
  7.        public AttributeTargets ValidOn { get; }  
  8.     }  
  9. }  
  10.  

可以看到,它有一個(gè)構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)含有一個(gè)AttributeTargets類型的位置參數(shù)(Positional Parameter) validOn,還有兩個(gè)命名參數(shù)(Named Parameter)。注意ValidOn屬性不是一個(gè)命名參數(shù),因?yàn)樗话瑂et訪問器,(是位置參數(shù))。

這里大家一定疑惑為什么會(huì)這樣劃分參數(shù),這和特性的使用是相關(guān)的。假如AttributeUsageAttribute 是一個(gè)普通的類,我們一定是這樣使用的:

// 實(shí)例化一個(gè) AttributeUsageAttribute 類

AttributeUsageAttribute usage=new AttributeUsageAttribute(AttributeTargets.Class);

usage.AllowMultiple = true;  // 設(shè)置AllowMutiple屬性

usage.Inherited = false;// 設(shè)置Inherited屬性

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

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

可以看出,AttributeTargets.Class是構(gòu)造函數(shù)參數(shù)(位置參數(shù)),而AllowMutiple 和 Inherited實(shí)際上是屬性(命名參數(shù))。命名參數(shù)是可選的。將來我們的RecordAttribute的使用方式于此相同。(為什么管他們叫參數(shù),我猜想是因?yàn)樗鼈兊氖褂梅绞娇瓷先ジ袷欠椒ǖ膮?shù)吧。)

假設(shè)現(xiàn)在我們的RecordAttribute已經(jīng)OK了,則它的使用應(yīng)該是這樣的:

C#代碼

  1. [RecordAttribute("創(chuàng)建","張子陽(yáng)","2008-1-15",Memo="這個(gè)類僅供演示")]     
  2. public class DemoClass{     
  3.     // ClassBody     
  4. }     

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

C#自定義特性:AttributeTargets 位標(biāo)記

從AttributeUsage特性的名稱上就可以看出它用于描述特性的使用方式。具體來說,首先應(yīng)該是其所標(biāo)記的特性可以應(yīng)用于哪些類型或者對(duì)象。從上面的代碼,我們看到AttributeUsage特性的構(gòu)造函數(shù)接受一個(gè) AttributeTargets 類型的參數(shù),那么我們現(xiàn)在就來了解一下AttributeTargets。

AttributeTargets 是一個(gè)位標(biāo)記,它定義了特性可以應(yīng)用的類型和對(duì)象。

[Flags]

public enum AttributeTargets {

    Assembly = 1,         //可以對(duì)程序集應(yīng)用屬性。

    Module = 2,              //可以對(duì)模塊應(yīng)用屬性。

    Class = 4,            //可以對(duì)類應(yīng)用屬性。

    Struct = 8,              //可以對(duì)結(jié)構(gòu)應(yīng)用屬性,即值類型。

    Enum = 16,            //可以對(duì)枚舉應(yīng)用屬性。

    Constructor = 32,     //可以對(duì)構(gòu)造函數(shù)應(yīng)用屬性。

    Method = 64,          //可以對(duì)方法應(yīng)用屬性。

    Property = 128,           //可以對(duì)屬性 (Property) 應(yīng)用屬性 (Attribute)。

    Field = 256,          //可以對(duì)字段應(yīng)用屬性。

    Event = 512,          //可以對(duì)事件應(yīng)用屬性。

    Interface = 1024,            //可以對(duì)接口應(yīng)用屬性。

    Parameter = 2048,            //可以對(duì)參數(shù)應(yīng)用屬性。

    Delegate = 4096,             //可以對(duì)委托應(yīng)用屬性。

    ReturnValue = 8192,             //可以對(duì)返回值應(yīng)用屬性。

    GenericParameter = 16384,    //可以對(duì)泛型參數(shù)應(yīng)用屬性。

    All = 32767,  //可以對(duì)任何應(yīng)用程序元素應(yīng)用屬性。

}

現(xiàn)在應(yīng)該不難理解為什么上面我范例中用的是:

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

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

[AttributeUsage(6140, Inherited = false)]

因?yàn)锳ttributeUsage是一個(gè)位標(biāo)記,所以可以使用按位或“|”來進(jìn)行組合。所以,當(dāng)我們這樣寫時(shí):

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)

意味著既可以將特性應(yīng)用到類上,也可以應(yīng)用到接口上。

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

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

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

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

[RecordAttribute("創(chuàng)建","張子陽(yáng)","2008-1-15",Memo="這個(gè)類僅供演示")]

public class DemoClass{

// ClassBody

}

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

Inherited 就更復(fù)雜一些了,假如有一個(gè)類繼承自我們的DemoClass,那么當(dāng)我們將RecordAttribute添加到DemoClass上時(shí),DemoClass的子類也會(huì)獲得該特性。而當(dāng)特性應(yīng)用于一個(gè)方法,如果繼承自該類的子類將這個(gè)方法覆蓋,那么Inherited則用于說明是否子類方法是否繼承這個(gè)特性。

在我們的例子中,將 Inherited 設(shè)為false。

C#自定義特性:實(shí)現(xiàn) RecordAttribute

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

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

public class RecordAttribute:Attribute {

    // 略

}

C#自定義特性:使用 RecordAttribute

我們已經(jīng)創(chuàng)建好了自己的自定義特性,現(xiàn)在是時(shí)候使用它了。

C#代碼

  1. [Record("更新""Matthew""2008-1-20", Memo = "修改 ToString()方法")]     
  2. [Record("更新""Jimmy""2008-1-18")]     
  3. [Record("創(chuàng)建""張子陽(yáng)""2008-1-15")]     
  4. public class DemoClass {          
  5.     public override string ToString() {     
  6.        return "This is a demo class";     
  7.     }     
  8. }     
  9.     
  10. class Program {     
  11.     static void Main(string[] args) {     
  12.        DemoClass demo = new DemoClass();     
  13.        Console.WriteLine(demo.ToString());     
  14.     }     
  15. }    

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

可以通過IL DASM看到 

【編輯推薦】

  1. C#基礎(chǔ)知識(shí)一覽
  2. 學(xué)習(xí)C#自定義用戶控件
  3. C#自定義組件和用戶組件屬性的設(shè)置
  4. C#編程中的組件-事件-委托
  5. Visual C#自定義組件的設(shè)計(jì):Pop3Com組件
責(zé)任編輯:book05 來源: cnblogs
相關(guān)推薦

2009-08-04 09:09:51

C#反射

2009-08-28 17:45:19

C#自定義數(shù)據(jù)

2009-08-04 12:56:51

C#自定義事件

2021-03-29 00:02:10

C#Attribute元素

2009-09-03 15:46:57

C#自定義事件

2009-08-03 13:34:06

自定義C#控件

2009-08-03 13:39:46

C#自定義用戶控件

2009-08-04 08:48:44

C#內(nèi)置特性

2009-08-05 17:03:37

C#自定義控件

2021-06-17 06:52:37

C#自定義異常

2009-08-04 09:56:46

C#事件處理自定義事件

2009-08-04 12:40:34

c#自定義事件

2009-08-12 14:53:50

C#類型轉(zhuǎn)換函數(shù)

2024-09-11 14:46:48

C#旋轉(zhuǎn)按鈕

2009-08-04 13:31:35

C#自定義事件

2009-08-05 18:01:20

C#自定義異常處理

2009-08-17 17:24:02

C#自定義消息框

2009-08-04 13:07:46

C#自定義快捷鍵

2009-08-19 16:51:14

C# 4.0 dyna

2009-08-05 17:15:27

C#自定義按鈕
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)