亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

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

2019-11-11 04:25:01
字體:
來源:轉載
供稿:網友

反射特性(Attribute)

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

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

我們很快就發現這樣將參數一個個羅列到方法的參數列表中擴展性很糟糕,我們最好定義一個Message類將短消息封裝起來,然后給方法傳遞一個Message對象:

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

此時,我們或許應該將舊的方法刪除,用這個擴展性更好的SendMsg方法來取代。遺憾的是我們往往不能,因為這組程序可能作為一組API發布,在很多客戶程序中已經在使用舊版本的SendMsg()方法,如果我們在更新程序的時候簡單地刪除掉舊的SendMsg()方法,那么將造成使用老版本SendMsg()方法的客戶程序不能工作。

這個時候,我們該如果做呢?我們當然可以通過方法重載來完成,這樣就不用刪除舊的SendMsg()方法了。但是如果新的SendMsg()不僅優化了參數的傳遞,并且在算法和效率上也進行了全面的優化,那么我們將會迫切希望告知客戶程序現在有一個全新的高性能SendMsg()方法可供使用,但此時客戶程序并不知道已經存在一個新的SendMsg方法,我們又該如何做呢?我們可以打電話告訴維護客戶程序的程序員,或者發電子郵件給他,但這樣顯然不夠方便,最好有一種辦法能讓他一編譯項目,只要存在對舊版本SendMsg()方法的調用,就會被編譯器告知。

1..Net內置特性介紹

.Net 中可以使用特性來完成這一工作。特性是一個對象,它可以加載到程序集及程序集的對象中,這些對象包括 程序集本身、模塊、類、接口、結構、構造函數、方法、方法參數等,加載了特性的對象稱作特性的目標。特性是為程序添加元數據(描述數據的數據)的一種機制,通過它可以給編譯器提供指示或者提供對數據的說明。

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

1.1 System.ObsoleteAttribute 特性

我們通過這個例子來看一下特性是如何解決上面的問題:我們可以給舊的SendMsg()方法上面加上Obsolete特性來告訴編譯器這個方法已經過時,然后當編譯器發現當程序中有地方在使用這個用Obsolete標記過的方法時,就會給出一個警告信息。

namespace Attribute {    public classMessage {}        public classTestClass {       // 添加Obsolete特性       [Obsolete("請使用新的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());                }    }}

現在運行這段代碼,我們會發現編譯器給出了一個警告:警告CS0618: “Attribute.TestClass.ShowMsg()”已過時:“請使用新的SendMsg(Message msg)重載方法”。通過使用特性,我們可以看到編譯器給出了警告信息,告訴客戶程序存在一個新的方法可供使用,這樣,程序員在看到這個警告信息后,便會考慮使用新的SendMsg()方法。

NOTE:簡單起見,TestClass類和 Program位于同一個程序集中,實際上它們可以離得很遠。

1.2 特性的使用方法

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

NOTE:實際上,當你用鼠標框選住Obsolete,然后按下F12轉到定義,會發現它的全名是ObsoleteAttribute,繼承自Attribute類。但是這里卻僅用Obsolete來標記方法,這是.Net的一個約定,所有的特性應該均以Attribute來結尾,在為對象標記特性時如果沒有添加Attribute,編譯器會自動尋找帶有Attribute的版本。

NOTE:使用構造函數參數,參數的順序必須同構造函數聲明時的順序相同,所有在特性中也叫位置參數(Positional Parameters),與此相應,屬性參數也叫做命名參數(Named Parameters)。在下面會詳細說明。

2.自定義特性(Custom Attributes)

2.1 范例介紹

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

//更新:Matthew, 2008-2-10, 修改 ToString()方法//更新:Jimmy, 2008-1-18//創建:張子陽, 2008-1-15public classDemoClass{    // 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 sealedclassObsoleteAttribute :Attribute {       public ObsoleteAttribute();       public ObsoleteAttribute(string message);       public ObsoleteAttribute(string message,bool error);       public bool IsError { get; }       public string Message { get; }    }}

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

首先,我們應該發現,它繼承自Attribute類,這說明我們的RecordAttribute也應該繼承自Attribute類。

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

因為我們需要使用“元元數據”去描述我們定義的特性 RecordAttribute,所以現在我們需要首先了解一下“元元數據”。這里應該記得“元元數據”也是一個特性,大多數情況下,我們只需要掌握 AttributeUsage就可以了,所以現在就研究一下它。我們首先看上面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; }    }}

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

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

// 實例化一個 AttributeUsageAttribute 類AttributeUsageAttribute usage=newAttributeUsageAttribute(AttributeTargets.Class);usage.AllowMultiple = true;  // 設置AllowMutiple屬性usage.Inherited = false;// 設置Inherited屬性

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

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

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

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

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

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

2.3 AttributeTargets 位標記

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

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

[Flags]public enumAttributeTargets {    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)],另外這條語句必須位于程序語句開始之前。

2.4 Inherited 和 AllowMutiple屬性

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

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

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

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

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

2.5 實現 RecordAttribute

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

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple=true, Inherited=false)]public classRecordAttribute:Attribute {    // 略}

2.6 使用 RecordAttribute

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

[Record("更新", "Matthew", "2008-1-20", Memo = "修改 ToString()方法")][Record("更新", "Jimmy", "2008-1-18")][Record("創建", "張子陽", "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());    }}

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

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

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

class Program {    static void Main(string[] args) {       Type t = typeof(DemoClass);       Console.WriteLine("下面列出應用于 {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);           }       }    }}

輸出為:

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

好了,到了這一步,我想將這些數據錄入數據庫中將不再是個問題,我們關于反射自定義特性的章節也就到此為止了。

轉自張子陽:http://www.tracefact.net/CLR-and-Framework/Reflection-Part3.aspx


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日本精品视频在线观看| 日韩av不卡电影| 日日骚久久av| 久久精品99国产精品酒店日本| 欧美日韩国产限制| 激情亚洲一区二区三区四区| 91网站免费看| 欧美一级高清免费| 国产男人精品视频| 日韩欧亚中文在线| 国产精品一区二区三区成人| 日韩中文字幕视频在线| 黑人巨大精品欧美一区免费视频| 欧美国产在线电影| 欧美日韩精品在线观看| 亚洲国产高清高潮精品美女| 亚洲欧美在线第一页| 久久久之久亚州精品露出| xvideos成人免费中文版| 欧美成人精品在线观看| 91精品久久久久久久久久久久久| 亚洲国产天堂久久综合| 成人免费在线视频网址| 尤物九九久久国产精品的分类| 欧美裸身视频免费观看| 一区二区三区国产在线观看| 成人免费看吃奶视频网站| 久久中国妇女中文字幕| 久久精品亚洲94久久精品| 色在人av网站天堂精品| 亚洲国产一区二区三区四区| 久久精品成人欧美大片古装| 欧美韩日一区二区| 国产精品一香蕉国产线看观看| 亚洲成人久久久| 日韩在线一区二区三区免费视频| 日韩电影免费观看在线观看| 一区二区中文字幕| 欧美日韩不卡合集视频| 中文字幕亚洲无线码在线一区| 4p变态网欧美系列| 亚洲欧美中文日韩v在线观看| 91av在线免费观看| 欧美日韩福利视频| 91精品国产沙发| 国外成人在线视频| 中日韩美女免费视频网站在线观看| 欧美日韩亚洲高清| 亚洲一区二区免费在线| 欧美第一淫aaasss性| 日韩av电影在线网| 国产免费成人av| 欧洲s码亚洲m码精品一区| 麻豆精品精华液| 7m第一福利500精品视频| 热门国产精品亚洲第一区在线| 欧美大肥婆大肥bbbbb| 亚洲精选在线观看| 国产精品电影在线观看| 国产999精品久久久| 美女性感视频久久久| 久久激情视频久久| 精品女厕一区二区三区| 亚洲一区www| 疯狂做受xxxx高潮欧美日本| 国产精品视频久久久久| 欧美性猛交xxxx乱大交蜜桃| 午夜精品一区二区三区在线| 国产成人精品免高潮在线观看| 亚洲无亚洲人成网站77777| 国产91色在线|免| 日韩一级裸体免费视频| 日本一本a高清免费不卡| 欧美一级片在线播放| 久久中文字幕一区| 亚洲一区免费网站| 国产精品扒开腿做爽爽爽视频| 精品视频www| 亚洲欧洲在线视频| 亚洲综合大片69999| 欧美日韩精品在线播放| 欧美一级淫片播放口| 97视频在线观看免费| 久久久999精品免费| 日韩大片免费观看视频播放| 日本高清视频精品| 成人精品久久av网站| 欧美高清一级大片| 夜夜躁日日躁狠狠久久88av| 国产一区二区三区视频在线观看| 丝袜情趣国产精品| 国产精品久久久久久中文字| 欧美一区二区三区图| 欧美丰满少妇xxxx| 欧美日韩午夜激情| 国产欧美精品在线| 日韩a**站在线观看| 国产视频丨精品|在线观看| 夜夜嗨av色一区二区不卡| 亚州精品天堂中文字幕| 成人在线中文字幕| 成人av在线亚洲| 成人做爰www免费看视频网站| 夜色77av精品影院| 久久久成人精品视频| 国产在线视频91| 精品视频9999| 久久久久久国产精品三级玉女聊斋| 51ⅴ精品国产91久久久久久| 日本久久久a级免费| 久久久久成人网| 国内伊人久久久久久网站视频| 国产一区香蕉久久| 91精品国产乱码久久久久久蜜臀| 欧美一区二区影院| 日日噜噜噜夜夜爽亚洲精品| 欧美黄色免费网站| 欧美性xxxxxxx| 久久久av一区| 午夜精品www| 国产精品偷伦一区二区| 日韩精品极品毛片系列视频| 亚洲淫片在线视频| 欧美日韩亚洲一区二区三区| 国产精品久久久久av| 欧美大尺度在线观看| 欧美电影免费在线观看| 欧美日本精品在线| 麻豆国产精品va在线观看不卡| 亚洲欧美日韩国产中文专区| 亚洲а∨天堂久久精品9966| 日韩久久免费电影| 日韩国产欧美精品一区二区三区| 欧美高清一级大片| 日韩精品在线影院| 国产精品日韩精品| 精品一区电影国产| 国产成人精品久久| 欧美黑人一区二区三区| 日韩av在线网站| 一本色道久久综合亚洲精品小说| 91免费观看网站| 欧美大片免费观看| 日韩电影大全免费观看2023年上| 日日噜噜噜夜夜爽亚洲精品| 日本一区二区在线免费播放| 欧美三级xxx| 美日韩精品免费观看视频| 超碰91人人草人人干| 久久精品中文字幕电影| 亚洲久久久久久久久久| 9.1国产丝袜在线观看| 日韩成人激情在线| 日韩性生活视频| 色哟哟入口国产精品| 日韩中文字幕第一页| 91精品国产综合久久香蕉922| 欧美中文在线观看国产| 亚洲天堂成人在线| 国产一区二区成人| 亚洲福利在线视频| xvideos成人免费中文版| 在线播放国产一区中文字幕剧情欧美| 欧美中文在线观看|