国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

.Net 中的反射(反射特性)

2019-11-11 02:56:30
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

反射特性(Attribute)

可能很多人還不了解特性,所以我們先了解一下什么是特性。想想看如果有一個(gè)消息系統(tǒng),它存在這樣一個(gè)方法,用來(lái)將一則短消息發(fā)送給某人:

// title: 標(biāo)題;author:作者;content:內(nèi)容;receiverId:接受者Idpublic bool SendMsg(string title,string author,string content,int receiverId){    // Do Send Action}

我們很快就發(fā)現(xiàn)這樣將參數(shù)一個(gè)個(gè)羅列到方法的參數(shù)列表中擴(kuò)展性很糟糕,我們最好定義一個(gè)Message類將短消息封裝起來(lái),然后給方法傳遞一個(gè)Message對(duì)象:

public class Message{    PRivate string title;    private string author;    private string content;    private int receiverId;    // 略}public bool SendMsg(Messag msg){    // Do some Action}

此時(shí),我們或許應(yīng)該將舊的方法刪除,用這個(gè)擴(kuò)展性更好的SendMsg方法來(lái)取代。遺憾的是我們往往不能,因?yàn)檫@組程序可能作為一組API發(fā)布,在很多客戶程序中已經(jīng)在使用舊版本的SendMsg()方法,如果我們?cè)诟鲁绦虻臅r(shí)候簡(jiǎn)單地刪除掉舊的SendMsg()方法,那么將造成使用老版本SendMsg()方法的客戶程序不能工作。

這個(gè)時(shí)候,我們?cè)撊绻瞿??我們?dāng)然可以通過(guò)方法重載來(lái)完成,這樣就不用刪除舊的SendMsg()方法了。但是如果新的SendMsg()不僅優(yōu)化了參數(shù)的傳遞,并且在算法和效率上也進(jìn)行了全面的優(yōu)化,那么我們將會(huì)迫切希望告知客戶程序現(xiàn)在有一個(gè)全新的高性能SendMsg()方法可供使用,但此時(shí)客戶程序并不知道已經(jīng)存在一個(gè)新的SendMsg方法,我們又該如何做呢?我們可以打電話告訴維護(hù)客戶程序的程序員,或者發(fā)電子郵件給他,但這樣顯然不夠方便,最好有一種辦法能讓他一編譯項(xiàng)目,只要存在對(duì)舊版本SendMsg()方法的調(diào)用,就會(huì)被編譯器告知。

1..Net內(nèi)置特性介紹

.Net 中可以使用特性來(lái)完成這一工作。特性是一個(gè)對(duì)象,它可以加載到程序集及程序集的對(duì)象中,這些對(duì)象包括 程序集本身、模塊、類、接口、結(jié)構(gòu)、構(gòu)造函數(shù)、方法、方法參數(shù)等,加載了特性的對(duì)象稱作特性的目標(biāo)。特性是為程序添加元數(shù)據(jù)(描述數(shù)據(jù)的數(shù)據(jù))的一種機(jī)制,通過(guò)它可以給編譯器提供指示或者提供對(duì)數(shù)據(jù)的說(shuō)明。

NOTE:特性的英文名稱叫做Attribute,在有的書(shū)中,將它翻譯為“屬性”;另一些書(shū)中,將它翻譯為“特性”;由于通常我們將含有g(shù)et和/或set訪問(wèn)器的類成員稱為“屬性”(英文Property),所以本文中我將使用“特性”這個(gè)名詞,以區(qū)分“屬性”(Property)。      中文版的VS2005使用“屬性”。

1.1 System.ObsoleteAttribute 特性

我們通過(guò)這個(gè)例子來(lái)看一下特性是如何解決上面的問(wèn)題:我們可以給舊的SendMsg()方法上面加上Obsolete特性來(lái)告訴編譯器這個(gè)方法已經(jīng)過(guò)時(shí),然后當(dāng)編譯器發(fā)現(xiàn)當(dāng)程序中有地方在使用這個(gè)用Obsolete標(biāo)記過(guò)的方法時(shí),就會(huì)給出一個(gè)警告信息。

namespace Attribute {    public classMessage {}        public classTestClass {       // 添加Obsolete特性       [Obsolete("請(qǐng)使用新的SendMsg(Message msg)重載方法")]       public staticvoid ShowMsg() {           Console.WriteLine("這是舊的SendMsg()方法");       }       public staticvoid ShowMsg(Message msg) {           Console.WriteLine("新SendMsg()方法");       }    }    class Program {       static void Main(string[] args) {           TestClass.ShowMsg();           TestClass.ShowMsg(new Message());                }    }}

現(xiàn)在運(yùn)行這段代碼,我們會(huì)發(fā)現(xiàn)編譯器給出了一個(gè)警告:警告CS0618: “Attribute.TestClass.ShowMsg()”已過(guò)時(shí):“請(qǐng)使用新的SendMsg(Message msg)重載方法”。通過(guò)使用特性,我們可以看到編譯器給出了警告信息,告訴客戶程序存在一個(gè)新的方法可供使用,這樣,程序員在看到這個(gè)警告信息后,便會(huì)考慮使用新的SendMsg()方法。

NOTE:簡(jiǎn)單起見(jiàn),TestClass類和 Program位于同一個(gè)程序集中,實(shí)際上它們可以離得很遠(yuǎn)。

1.2 特性的使用方法

通過(guò)上面的例子,我們已經(jīng)大致看到特性的使用方法:首先是有一對(duì)方括號(hào)“[]”,在左方括號(hào)“[”后緊跟特性的名稱,比如Obsolete,隨后是一個(gè)圓括號(hào)“()”。和普通的類不同,這個(gè)圓括號(hào)不光可以寫入構(gòu)造函數(shù)的參數(shù),還可以給類的屬性賦值,在Obsolete的例子中,僅傳遞了構(gòu)造函數(shù)參數(shù)。

NOTE:實(shí)際上,當(dāng)你用鼠標(biāo)框選住Obsolete,然后按下F12轉(zhuǎn)到定義,會(huì)發(fā)現(xiàn)它的全名是ObsoleteAttribute,繼承自Attribute類。但是這里卻僅用Obsolete來(lái)標(biāo)記方法,這是.Net的一個(gè)約定,所有的特性應(yīng)該均以Attribute來(lái)結(jié)尾,在為對(duì)象標(biāo)記特性時(shí)如果沒(méi)有添加Attribute,編譯器會(huì)自動(dòng)尋找?guī)в蠥ttribute的版本。

NOTE:使用構(gòu)造函數(shù)參數(shù),參數(shù)的順序必須同構(gòu)造函數(shù)聲明時(shí)的順序相同,所有在特性中也叫位置參數(shù)(Positional Parameters),與此相應(yīng),屬性參數(shù)也叫做命名參數(shù)(Named Parameters)。在下面會(huì)詳細(xì)說(shuō)明。

2.自定義特性(Custom Attributes)

2.1 范例介紹

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

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

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

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

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

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

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

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

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

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

首先,我們應(yīng)該發(fā)現(xiàn),它繼承自Attribute類,這說(shuō)明我們的RecordAttribute也應(yīng)該繼承自Attribute類。

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

因?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的定義:

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

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

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

// 實(shí)例化一個(gè) AttributeUsageAttribute 類AttributeUsageAttribute usage=newAttributeUsageAttribute(AttributeTargets.Class);usage.AllowMultiple = true;  // 設(shè)置AllowMutiple屬性u(píng)sage.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ù)是可選的。將來(lái)我們的RecordAttribute的使用方式于此相同。(為什么管他們叫參數(shù),我猜想是因?yàn)樗鼈兊氖褂梅绞娇瓷先ジ袷欠椒ǖ膮?shù)吧。)

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

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

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

2.3 AttributeTargets 位標(biāo)記

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

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

[Flags]public enumAttributeTargets {    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)記,所以可以使用按位或“|”來(lái)進(jìn)行組合。所以,當(dāng)我們這樣寫時(shí):

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)

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

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

2.4 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 classDemoClass{// ClassBody}

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

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

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

2.5 實(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 classRecordAttribute:Attribute {    // 略}

2.6 使用 RecordAttribute

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

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

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

3.使用反射查看自定義特性

利用反射來(lái)查看 自定義特性信息 與 查看其他信息 類似,首先基于類型(本例中是DemoClass)獲取一個(gè)Type對(duì)象,然后調(diào)用Type對(duì)象的GetCustomAttributes()方法,獲取應(yīng)用于該類型上的特性。當(dāng)指定GetCustomAttributes(Type attributeType, bool inherit) 中的第一個(gè)參數(shù)attributeType時(shí),將只返回指定類型的特性,否則將返回全部特性;第二個(gè)參數(shù)指定是否搜索該成員的繼承鏈以查找這些屬性。

class Program {    static void Main(string[] args) {       Type t = typeof(DemoClass);       Console.WriteLine("下面列出應(yīng)用于 {0} 的RecordAttribute屬性:" , t);       // 獲取所有的RecordAttributes特性       object[] records = t.GetCustomAttributes(typeof(RecordAttribute),false);       foreach (RecordAttribute record in records) {           Console.WriteLine("   {0}", record);           Console.WriteLine("      類型:{0}", record.RecordType);           Console.WriteLine("      作者:{0}", record.Author);           Console.WriteLine("      日期:{0}", record.Date.ToShortDateString());           if(!String.IsNullOrEmpty(record.Memo)){              Console.WriteLine("      備注:{0}",record.Memo);           }       }    }}

輸出為:

下面列出應(yīng)用于 AttributeDemo.DemoClass 的RecordAttribute屬性:   AttributeDemo.RecordAttribute      類型:更新      作者:Matthew      日期:2008-1-20      備注:修改 ToString()方法   AttributeDemo.RecordAttribute      類型:更新      作者:Jimmy      日期:2008-1-18   AttributeDemo.RecordAttribute      類型:創(chuàng)建      作者:張子陽(yáng)      日期:2008-1-15

好了,到了這一步,我想將這些數(shù)據(jù)錄入數(shù)據(jù)庫(kù)中將不再是個(gè)問(wèn)題,我們關(guān)于反射自定義特性的章節(jié)也就到此為止了。

轉(zhuǎn)自張子陽(yáng):http://www.tracefact.net/CLR-and-Framework/Reflection-Part3.aspx


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阳谷县| 苏尼特左旗| 奉贤区| 苍南县| 始兴县| 河曲县| 探索| 曲阜市| 溧阳市| 山丹县| 图片| 马山县| 沂南县| 泉州市| 延寿县| 芜湖市| 鲁山县| 平陆县| 汤原县| 延津县| 漠河县| 长葛市| 威宁| 定日县| 教育| 宣城市| 阳西县| 马尔康县| 小金县| 陇南市| 安乡县| 城市| 无极县| 丰城市| 巴楚县| 武威市| 黄山市| 鞍山市| 辛集市| 瓦房店市| 山阴县|