欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
.Net 中的反射(反射特性)
.Net 中的反射(反射特性) - Part.3(轉載)
2008年03月22日 星期六 13:43

.Net 中的反射(反射特性) - Part.3

反射特性(Attribute)

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

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

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

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

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

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

1..Net內置特性介紹

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

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

1.1 System.ObsoleteAttribute 特性

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

namespace Attribute {

    public class Message {}
   
    public class TestClass {
       // 添加Obsolete特性
       [Obsolete("請使用新的SendMsg(Message msg)重載方法")]
       public static void ShowMsg() {
           Console.WriteLine("這是舊的SendMsg()方法");
       }

       public static void ShowMsg(Message msg) {
           Console.WriteLine("新SendMsg()方法");
       }

    }

    class Program {
       static void Main(string[] args) {
           TestClass.ShowMsg();
           TestClass.ShowMsg(new Message());         
       }
    }
}

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

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

1.2 特性的使用方法

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

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

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

2.自定義特性(Custom Attributes)

2.1 范例介紹

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

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

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

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

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

public class RecordAttribute {
    private string recordType;      // 記錄類(lèi)型:更新/創(chuàng )建
    private string author;          // 作者
    private DateTime date;          // 更新/創(chuàng )建 日期
    private string memo;         // 備注

    // 構造函數,構造函數的參數在特性中也稱(chēng)為“位置參數”。
    public RecordAttribute(string recordType, string author, string date) {
       this.recordType = recordType;
       this.author = author;
       this.date = Convert.ToDateTime(date);
    }

    // 對于位置參數,通常只提供get訪(fǎng)問(wèn)器
    public string RecordType {   get { return recordType; }   }
    public string Author { get { return author; } }
    public DateTime Date { get { return date; } }

    // 構建一個(gè)屬性,在特性中也叫“命名參數”
    public string Memo {
       get { return memo; }
       set { memo = value; }
    }
}

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

這個(gè)類(lèi)不光看上去,實(shí)際上也和普通的類(lèi)沒(méi)有任何區別,顯然不能它因為名字后面跟了個(gè)Attribute就搖身一變成了特性。那么怎樣才能讓它稱(chēng)為特性并應用到一個(gè)類(lèi)上面呢?進(jìn)行下一步之前,我們看看.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; }
    }
}

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

首先,我們應該發(fā)現,它繼承自Attribute類(lèi),這說(shuō)明我們的RecordAttribute也應該繼承自Attribute類(lèi)。

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

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

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

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

// 實(shí)例化一個(gè) AttributeUsageAttribute 類(lèi)
AttributeUsageAttribute usage=new AttributeUsageAttribute(AttributeTargets.Class)
;
usage.AllowMultiple = true; // 設置AllowMutiple屬性
usage.Inherited = false;// 設置Inherited屬性

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

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

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

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

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

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

2.3 AttributeTargets 位標記

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

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

[Flags]
public enum AttributeTargets {

    Assembly = 1,         //可以對程序集應用屬性。
    Module = 2,              //可以對模塊應用屬性。
    Class = 4,            //可以對類(lèi)應用屬性。
    Struct = 8,              //可以對結構應用屬性,即值類(lèi)型。
    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是一個(gè)位標記,所以可以使用按位或“|”來(lái)進(jìn)行組合。所以,當我們這樣寫(xiě)時(shí):

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)

意味著(zhù)既可以將特性應用到類(lèi)上,也可以應用到接口上。

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

2.4 Inherited 和 AllowMutiple屬性

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

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

所以,我們必須顯示的將AllowMutiple設置為T(mén)rue。

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

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

2.5 實(shí)現 RecordAttribute

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

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

2.6 使用 RecordAttribute

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

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

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

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

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

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("      類(lèi)型:{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
      類(lèi)型:更新
      作者:Matthew
      日期:2008-1-20
      備注:修改 ToString()方法
   AttributeDemo.RecordAttribute
      類(lèi)型:更新
      作者:Jimmy
      日期:2008-1-18
   AttributeDemo.RecordAttribute
      類(lèi)型:創(chuàng )建
      作者:張子陽(yáng)
      日期:2008-1-15

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

轉載地址:http://www.tracefact.net/CLR-and-Framework/Reflection-Part3.aspx

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
C#反射的特性
[你必須知道的.NET] 第三回:歷史糾葛:特性和屬性
C# 中特性(Attribute)的使用簡(jiǎn)介
C# 特性詳解
C# 特性(Attribute) | 菜鳥(niǎo)教程
Attributes in c#
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久