




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、第13章 反 射反射是一個(gè)普通術(shù)語(yǔ),描述了在運(yùn)行過(guò)程中檢查和處理程序元素的功能。例如,反射允許完成以下任務(wù): 枚舉類型的成員 實(shí)例化新對(duì)象 執(zhí)行對(duì)象的成員 查找類型的信息 查找程序集的信息 檢查應(yīng)用于類型的定制特性 創(chuàng)建和編譯新程序集這個(gè)列表列出了許多功能,包括.NET Framework 類庫(kù)提供的一些最強(qiáng)大、最復(fù)雜的功能。但本章不可能介紹反射的所有功能,僅討論最常用的功能。首先討論定制特性,定制特性允許把定制的元數(shù)據(jù)與程序元素關(guān)聯(lián)起來(lái)。這些元數(shù)據(jù)是在編譯過(guò)程中創(chuàng)建的,并嵌入到程序集中。接著就可以在運(yùn)行期間使用反射的一些功能檢查這些元數(shù)據(jù)了。在介紹了定制特性后,本章將探討支持反射的一些基類,
2、包括System.Type 和System.Reflection.Assembly 類,它們可以訪問(wèn)反射提供的許多功能。為了演示定制特性和反射,我們將開發(fā)一個(gè)示例,說(shuō)明公司如何定期升級(jí)軟件,自動(dòng)解釋升級(jí)的信息。在這個(gè)示例中,要定義幾個(gè)定制特性,表示程序元素最后修改或創(chuàng)建的日期,以及發(fā)生了什么變化。然后使用反射開發(fā)一個(gè)應(yīng)用程序,在程序集中查找這些特性,自動(dòng)顯示軟件自某個(gè)給定日期以來(lái)升級(jí)的所有信息。本章要討論的另一個(gè)示例是一個(gè)應(yīng)用程序,該程序讀寫數(shù)據(jù)庫(kù),并使用定制特性,把類和特性標(biāo)記為對(duì)應(yīng)的數(shù)據(jù)庫(kù)表和列。然后在運(yùn)行期間從程序集中讀取這些特性,使程序可以自動(dòng)從數(shù)據(jù)庫(kù)的相應(yīng)位置檢索或?qū)懭霐?shù)據(jù),無(wú)需為每
3、個(gè)表或列編寫特定的邏輯。第部分 C# 語(yǔ)言13.1 定制特性前面介紹了如何在程序的各個(gè)數(shù)據(jù)項(xiàng)上定義特性。這些特性都是Microsoft 定義好的,作為.NET Framework 類庫(kù)的一部分,許多特性都得到了C#編譯器的支持。對(duì)于這些特性,編譯器可以以特殊的方式定制編譯過(guò)程,例如,可以根據(jù)StructLayout 特性中的信息在內(nèi)存中布置結(jié)構(gòu)。.NET Framework 也允許用戶定義自己的特性。顯然,這些特性不會(huì)影響編譯過(guò)程,因?yàn)榫幾g器不能識(shí)別它們,但這些特性在應(yīng)用于程序元素時(shí),可以在編譯好的程序集中用作元數(shù)據(jù)。這些元數(shù)據(jù)在文檔說(shuō)明中非常有用。但是,使定制特性非常強(qiáng)大的因素是使用反射,代
4、碼可以讀取這些元數(shù)據(jù),使用它們?cè)谶\(yùn)行期間作出決策,也就是說(shuō),定制特性可以直接影響代碼運(yùn)行的方式。例如,定制特性可以用于支持對(duì)定制許可類進(jìn)行聲明代碼訪問(wèn)安全檢查,把信息與程序元素關(guān)聯(lián)起來(lái),由測(cè)試工具使用,或者在開發(fā)可擴(kuò)展的架構(gòu)時(shí),允許加載插件或模塊。13.1.1 編寫定制特性為了理解編寫定制特性的方式,應(yīng)了解一下在編譯器遇到代碼中某個(gè)應(yīng)用了定制特性的元素時(shí),該如何處理。以數(shù)據(jù)庫(kù)為例,假定有一個(gè)C#屬性聲明,如下所示。FieldName("SocialSecurityNumber")public string SocialSecurityNumberget / etc.當(dāng)C#編
5、譯器發(fā)現(xiàn)這個(gè)屬性有一個(gè)特性FieldName 時(shí),首先會(huì)把字符串Attribute 添加到這個(gè)名稱的后面,形成一個(gè)組合名稱FieldNameAttribute,然后在其搜索路徑的所有命名空間(即在using 語(yǔ)句中提及的命名空間)中搜索有指定名稱的類。但要注意,如果用一個(gè)特性標(biāo)記數(shù)據(jù)項(xiàng),而該特性的名稱以字符串Attribute 結(jié)尾,編譯器就不會(huì)把該字符串加到組合名稱中,而是不修改該特性名。因此,上面的代碼實(shí)際上等價(jià)于:FieldNameAttribute("SocialSecurityNumber")public string SocialSecurityNumberge
6、t / etc.編譯器會(huì)找到含有該名稱的類,且這個(gè)類直接或間接派生自System.Attribute。編譯器還認(rèn)為這個(gè)類包含控制特性用法的信息。特別是屬性類需要指定: 特性可以應(yīng)用到哪些程序元素上(類、結(jié)構(gòu)、屬性和方法等) 它是否可以多次應(yīng)用到同一個(gè)程序元素上 特性在應(yīng)用到類或接口上時(shí),是否由派生類和接口繼承322第12 章反 射 這個(gè)特性有哪些必選和可選參數(shù)如果編譯器找不到對(duì)應(yīng)的特性類,或者找到一個(gè)這樣的特性類,但使用特性的方式與特性類中的信息不匹配,編譯器就會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤。例如,如果特性類指定該特性只能應(yīng)用于字段,但我們把它應(yīng)用到結(jié)構(gòu)定義上,就會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤。繼續(xù)上面的示例,假定定
7、義了一個(gè)FieldName 特性:AttributeUsage(AttributeTargets.Property,AllowMultiple=false,Inherited=false)public class FieldNameAttribute : Attributeprivate string name;public FieldNameAttribute(string name) = name;下面幾節(jié)討論這個(gè)定義中的每個(gè)元素。1. AttributeUsage 特性要注意的第一個(gè)問(wèn)題是特性(attribute)類本身用一個(gè)特性System.AttributeUsag
8、e 來(lái)標(biāo)記。這是Microsoft 定義的一個(gè)特性,C#編譯器為它提供了特殊的支持(AttributeUsage 根本不是一個(gè)特性,它更像一個(gè)元特性,因?yàn)樗荒軕?yīng)用到其他特性上,不能應(yīng)用到類上)。AttributeUsage 主要用于表示定制特性可以應(yīng)用到哪些類型的程序元素上。這些信息由它的第一個(gè)參數(shù)給出,該參數(shù)是必選的,其類型是枚舉類型AttributeTargets。在上面的示例中,指定FieldName 特性只能應(yīng)用到屬性(property)上 這是因?yàn)槲覀冊(cè)谇懊娴拇a段中把它應(yīng)用到屬性上。AttributeTargets 枚舉的成員如下: All Assembly Class Cons
9、tructor Delegate Enum Event Field GenericParameter(僅.NET 2.0 提供) Interface Method Module Parameter Property323第部分 C# 語(yǔ)言 ReturnValue Struct這個(gè)列表列出了可以應(yīng)用該特性的所有程序元素。注意在把特性應(yīng)用到程序元素上時(shí),應(yīng)把特性放在元素前面的方括號(hào)中。但是,在上面的列表中,有兩個(gè)值不對(duì)應(yīng)于任何程序元素:Assembly 和Module。特性可以作為一個(gè)整體應(yīng)用到程序集或模塊中,而不是應(yīng)用到代碼中的一個(gè)元素上,在這種情況下,這個(gè)特性可以放在源代碼的任何地方,但需要用
10、關(guān)鍵字assembly 或module 來(lái)做前綴:assembly: SomeAssemblyAttribute(Parameters)module: SomeAssemblyAttribute(Parameters)在指定定制特性的有效目標(biāo)元素時(shí),可以使用按位OR 運(yùn)算符把這些值組合起來(lái)。例如,如果指定FieldName 特性可以應(yīng)用到屬性和字段上,可以編寫下面的代碼:AttributeUsage(AttributeTargets.Property | AttributeTargets.Field,AllowMultiple=false,Inherited=false)public clas
11、s FieldNameAttribute : Attribute也可以使用AttributeTargets.All 指定特性可以應(yīng)用到所有類型的程序元素上。AttributesUsage 特性還包含另外兩個(gè)參數(shù)AllowMultiple 和 Inherited。它們用不同的語(yǔ)法來(lái)指定:<AttributeName>=<AttributeValue>,而不是只給出這些參數(shù)的值。這些參數(shù)是可選的,如果需要,可以忽略它們。AllowMultiple 參數(shù)表示一個(gè)特性是否可以多次應(yīng)用到同一項(xiàng)上,這里把它設(shè)置為false,表示如果編譯器遇到下述代碼,就會(huì)產(chǎn)生一個(gè)錯(cuò)誤:FieldN
12、ame("SocialSecurityNumber")FieldName("NationalInsuranceNumber")public string SocialSecurityNumber/ etc.如果Inherited 參數(shù)設(shè)置為true,就表示應(yīng)用到類或接口上的特性也可以自動(dòng)應(yīng)用到所有派生的類或接口上。如果特性應(yīng)用到方法或?qū)傩陨?,也可以自?dòng)應(yīng)用到該方法或?qū)傩缘闹剌d上。2. 指定特性參數(shù)下面介紹如何指定定制特性的參數(shù)。在編譯器遇到下述語(yǔ)句時(shí):FieldName("SocialSecurityNumber")public st
13、ring SocialSecurityNumber/ etc.會(huì)檢查傳送給特性的參數(shù)(在本例中,是一個(gè)字符串),并查找該特性中帶這些參數(shù)的324第12 章反 射構(gòu)造函數(shù)。如果找到一個(gè)這樣的構(gòu)造函數(shù),編譯器就會(huì)把指定的元數(shù)據(jù)傳送給程序集。如果找不到,就生成一個(gè)編譯錯(cuò)誤。如后面所述,反射會(huì)從程序集中讀取元數(shù)據(jù),并實(shí)例化它們表示的特性類。因此,編譯器需要確保存在這樣的構(gòu)造函數(shù),才能在運(yùn)行期間實(shí)例化指定的特性。在本例中,僅為FieldNameAttribute 提供了一個(gè)構(gòu)造函數(shù),而這個(gè)構(gòu)造函數(shù)有一個(gè)字符串參數(shù)。因此,在把FieldNameAttribute 特性應(yīng)用到一個(gè)屬性上時(shí),必須為它提供一個(gè)字
14、符串參數(shù),如上面的代碼所示。如果可以選擇特性的參數(shù)類型,當(dāng)然可以提供構(gòu)造函數(shù)的不同重載方法,但一般是僅提供一個(gè)構(gòu)造函數(shù),使用屬性來(lái)定義其他可選參數(shù),下面將介紹可選參數(shù)。3. 指定特性的可選參數(shù)在 AttributeUsage 特性中,可以使用另一個(gè)語(yǔ)法,把可選參數(shù)添加到特性中。這個(gè)語(yǔ)法指定可選參數(shù)的名稱和值,處理特性類中的公共屬性或字段。例如,假定修改SocialSecurityNumber 屬性的定義,如下所示:FieldName("SocialSecurityNumber", Comment="This is the primary key field&quo
15、t;)public string SocialSecurityNumber/ etc.在本例中,編譯器識(shí)別第二個(gè)參數(shù)的語(yǔ)法<ParameterName>=<ParameterValue>,所以不會(huì)把這個(gè)參數(shù)傳遞給FieldNameAttribute 構(gòu)造函數(shù),而是查找一個(gè)有該名稱的公用屬性或字段(最好不要使用公用字段,所以一般情況下要使用屬性),編譯器可以用這個(gè)屬性設(shè)置第二個(gè)參數(shù)的值。如果希望上面的代碼工作,必須給FieldNameAttribute 添加一些代碼:AttributeUsage(AttributeTargets.Property,AllowMultipl
16、e=false,Inherited=false)public class FieldNameAttribute : Attributeprivate string comment;public string Commentgetreturn comment;setcomment = value;/ etc.325第部分 C# 語(yǔ)言13.1.2 定制特性示例:WhatsNewAttributes本節(jié)開始編寫前面描述過(guò)的示例WhatsNewAttributes,該示例提供了一個(gè)特性,表示最后一次修改程序元素的時(shí)間。這個(gè)示例比前面所有的示例都復(fù)雜,因?yàn)樗? 個(gè)不同的程序集: WhatsNewAt
17、tributes 程序集,它包含特性的定義。 VectorClass 程序集,包含所應(yīng)用的特性的代碼。 LookUpWhatsNew 程序集,包含顯示已改變的數(shù)據(jù)項(xiàng)信息的項(xiàng)目。當(dāng)然,只有LookUpWhatsNew 是前面使用的一個(gè)控制臺(tái)應(yīng)用程序,其余兩個(gè)程序集都是庫(kù)文件,它們都包含類的定義,但都沒有程序的入口。對(duì)于VectorClass 程序集,我們使用了VectorAsCollection 示例,但刪除了入口和測(cè)試代碼類,只剩下Vector 類。在命令行上編譯,以此管理3 個(gè)相關(guān)的程序集要求較高的技巧,所以我們分別給出編譯這3 個(gè)源文件的命令。也可以編輯代碼示例,(可以從Wrox Pres
18、s 網(wǎng)站上下載),組合為一個(gè)Visual Studio 2005 解決方案,詳見第14 章。下載的文件包含所需的Visual Studio 2005解決方案文件。1. WhatsNewAttributes 庫(kù)程序集首先從核心的 WhatsNewAttributes 程序集開始。其源代碼包含在文件WhatsNewAttributes.cs中,該文件位于本章示例代碼的WhatsNewAttributes 解決方案的WhatsNewAttributes項(xiàng)目中。編譯為庫(kù)的語(yǔ)法非常簡(jiǎn)單:在命令行上,給編譯器提供標(biāo)記target:library 即可。要編譯WhatsNewAttributes,鍵入:cs
19、c /target:library WhatsNewAttributes.csWhatsNewAttributes.cs 文件定義了兩個(gè)特性類LastModifiedAttribute 和SupportsWhatsNew-Attribute。LastModifiedAttribute 特性可以用于標(biāo)記最后一次修改數(shù)據(jù)項(xiàng)的時(shí)間,它有兩個(gè)必選參數(shù)(該參數(shù)傳遞給構(gòu)造函數(shù));修改的日期和包含描述修改的字符串。它還有一個(gè)可選參數(shù)Issues (表示存在一個(gè)公共屬性),它可以描述該數(shù)據(jù)項(xiàng)的任何重要問(wèn)題。在現(xiàn)實(shí)生活中,或許想把特性應(yīng)用到任何對(duì)象上。為了使代碼比較簡(jiǎn)單,這里僅允許將它應(yīng)用于類和方法,并允許它多
20、次應(yīng)用到同一項(xiàng)上(AllowMultiple=true),因?yàn)榭梢远啻涡薷囊粋€(gè)項(xiàng),每次修改都需要用一個(gè)不同的特性實(shí)例來(lái)標(biāo)記。SupportsWhatsNew 是一個(gè)較小的類,表示不帶任何參數(shù)的特性。這個(gè)特性是一個(gè)程序集的特性,用于把程序集標(biāo)記為通過(guò)LastModifiedAttribute 維護(hù)的文檔說(shuō)明書。這樣,以后查看這個(gè)程序集的程序會(huì)知道,它讀取的程序集是我們使用自動(dòng)文檔說(shuō)明過(guò)程生成的那個(gè)程序集。這部分示例的完整源代碼如下所示:using System;AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,Allo
21、wMultiple=true, Inherited=false)326第12 章反 射public class LastModifiedAttribute : Attributeprivate DateTime dateModified;private string changes;private string issues;public LastModifiedAttribute(string dateModified, string changes)this.dateModified = DateTime.Parse(dateModified);this.changes = changes
22、;public DateTime DateModifiedgetreturn dateModified;public string Changesgetreturn changes;public string Issuesgetreturn issues;setissues = value;AttributeUsage(AttributeTargets.Assembly)public class SupportsWhatsNewAttribute : Attribute從上面的描述可以看出,上面的代碼非常簡(jiǎn)單。但要注意,不必將set 訪問(wèn)器提供給Changes 和DateModified 屬性
23、,不需要這些訪問(wèn)器是因?yàn)樵跇?gòu)造函數(shù)中,這些參數(shù)都是必選參數(shù)。需要get 訪問(wèn)器,是因?yàn)橐院罂梢宰x取這些特性的值。327第部分 C# 語(yǔ)言2. VectorClass 程序集本節(jié)就使用這些特性,我們用前面的VectorAsCollection 示例的修訂版本來(lái)說(shuō)明。注意這里需要引用剛才創(chuàng)建的WhatsNewAttributes 庫(kù),還需要使用using 語(yǔ)句指定相應(yīng)的命名空間,這樣編譯器才能識(shí)別出這些特性:using System;using System.Collections;using System.Text;using Wrox.ProCSharp.WhatsNewAttributes;
24、assembly: SupportsWhatsNew在這段代碼中,添加了一行用SupportsWhatsNew 特性標(biāo)記程序集本身的代碼。下面考慮Vector 類的代碼。我們并不是真的要修改這個(gè)類中的任何內(nèi)容,只是添加兩個(gè)LastModified 特性,以標(biāo)記出本章對(duì)Vector 類進(jìn)行的操作。把Vector 定義為一個(gè)類,而不是結(jié)構(gòu),以簡(jiǎn)化后面顯示特性所編寫的代碼(在VectorAsCollection 示例中,Vector 是一個(gè)結(jié)構(gòu),但其枚舉器是一個(gè)類。于是,這個(gè)示例的下一個(gè)版本在查看程序集時(shí),必須同時(shí)考慮類和結(jié)構(gòu)。這會(huì)使例子比較復(fù)雜)。LastModified("14 Feb
25、 2007", "IEnumerable interface implemented " +"So Vector can now be treated as a collection")LastModified("10 Feb 2007", "IFormattable interface implemented " +"So Vector now responds to format specifiers N and VE")class Vector : IFormattable, I
26、Enumerablepublic double x, y, z;public Vector(double x, double y, double z)this.x = x;this.y = y;this.z = z;LastModified("10 Feb 2002","Method added in order to provide formatting support")public string ToString(string format, IFormatProvider formatProvider)if (format = null)retu
27、rn ToString();再把包含的VectorEnumerator 類標(biāo)記為new:LastModified("14 Feb 2007","Class created as part of collection support for Vector")328第12 章反 射private class VectorEnumerator : IEnumerator為了在命令行上編譯這段代碼,應(yīng)鍵入下面的命令:csc /target:library /reference:WhatsNewAttributes.dll VectorClass.cs上面是這個(gè)示
28、例的代碼。目前還不能運(yùn)行它,因?yàn)槲覀冎挥袃蓚€(gè)庫(kù)。在描述了反射的工作原理后,就介紹這個(gè)示例的最后一部分,查找和顯示這些特性。13.2 反射本節(jié)先介紹System.Type 類,通過(guò)這個(gè)類可以訪問(wèn)任何給定數(shù)據(jù)類型的信息。然后簡(jiǎn)要介紹System.Reflection.Assembly 類,它可以用于訪問(wèn)給定程序集的信息,或者把這個(gè)程序集加載到程序中。最后把本節(jié)的代碼和上一節(jié)的代碼結(jié)合起來(lái),完成WhatsNewAttributes示例。13.2.1 System.Type 類在本書中的許多場(chǎng)合中都使用了 Type 類,但它只存儲(chǔ)類型的引用:Type t = typeof(double)我們以前把Ty
29、pe 看作一個(gè)類,但它實(shí)際上是一個(gè)抽象的基類。只要實(shí)例化了一個(gè)Type對(duì)象,就實(shí)例化了Type 的一個(gè)派生類。Type 有與每種數(shù)據(jù)類型對(duì)應(yīng)的派生類,但一般情況下派生的類只提供各種Type 方法和屬性的不同重載,返回對(duì)應(yīng)數(shù)據(jù)類型的正確數(shù)據(jù)。一般不增加新的方法或?qū)傩?。獲取指向給定類型的Type 引用有3 種常用方式: 使用C#的typeof 運(yùn)算符,如上所示。這個(gè)運(yùn)算符的參數(shù)是類型的名稱(不放在引號(hào)中)。 使用GetType()方法,所有的類都會(huì)從System.Object 繼承這個(gè)類。double d = 10;Type t = d.GetType();在一個(gè)變量上調(diào)用GetType(),而不
30、是把類型的名稱作為其參數(shù)。但要注意,返回的Type 對(duì)象仍只與該數(shù)據(jù)類型相關(guān):它不包含與類型實(shí)例相關(guān)的任何信息。如果有一個(gè)對(duì)象引用,但不能確保該對(duì)象實(shí)際上是哪個(gè)類的實(shí)例,這個(gè)方法也是很有用的。 還可以調(diào)用Type 類的靜態(tài)方法GetType():Type t = Type.GetType("System.Double");Type 是許多反射技術(shù)的入口。它執(zhí)行許多方法和屬性,這里不可能列出所有的方法和屬性,而主要介紹如何使用這個(gè)類。注意,可用的屬性都是只讀的:可以使用Type 確定329第部分 C# 語(yǔ)言數(shù)據(jù)的類型,但不能使用它修改該類型!1. Type 的屬性由 Type
31、 執(zhí)行的屬性可以分為下述3 類: 有許多屬性都可以獲取包含與類相關(guān)的各種名稱的字符串,如表12-1 所示。表 12-1屬 性 返 回 值Name 數(shù)據(jù)類型名FullName 數(shù)據(jù)類型的完全限定名(包括命名空間名)Namespace 定義數(shù)據(jù)類型的命名空間名 屬性還可以進(jìn)一步獲取Type 對(duì)象的引用,這些引用表示相關(guān)的類,如表12-2 所示。表 12-2屬 性 返回對(duì)應(yīng)的Type 引用BaseType 這個(gè)Type 的直接基本類型UnderlyingSystemType 這個(gè)Type 在 .NET 運(yùn)行庫(kù)中映射的類型 (某些.NET 基類實(shí)際上映射由IL 識(shí)別的特定預(yù)定義類型) 許多Boolea
32、n 屬性表示這個(gè)類型是一個(gè)類、還是一個(gè)枚舉等。這些屬性包括IsAbstract、IsArray、IsClass、IsEnum、IsInterface、IsPointer、IsPrimitive(一種預(yù)定義的基本數(shù)據(jù)類型)、IsPublic、IsSealed 和IsValueType例如,使用一個(gè)基本數(shù)據(jù)類型:Type intType = typeof(int);Console.WriteLine(intType.IsAbstract); / writes falseConsole.WriteLine(intType.IsClass); / writes falseConsole.WriteLi
33、ne(intType.IsEnum); / writes falseConsole.WriteLine(intType.IsPrimitive); / writes trueConsole.WriteLine(intType.IsValueType); / writes true或者使用Vector 類:Type intType = typeof(Vector);Console.WriteLine(intType.IsAbstract); / writes falseConsole.WriteLine(intType.IsClass); / writes trueConsole.WriteLi
34、ne(intType.IsEnum); / writes falseConsole.WriteLine(intType.IsPrimitive); / writes falseConsole.WriteLine(intType.IsValueType); / writes false也可以獲取定義類型的程序集的引用,該引用作為System.Reflection.Assembly 類實(shí)例的一個(gè)引用來(lái)返回:330第12 章反 射Type t = typeof (Vector);Assembly containingAssembly = new Assembly(t);2. 方法System.Typ
35、e 的大多數(shù)方法都用于獲取對(duì)應(yīng)數(shù)據(jù)類型的成員信息:構(gòu)造函數(shù)、屬性、方法和事件等。它有許多方法,但它們都有相同的模式。例如,有兩個(gè)方法可以獲取數(shù)據(jù)類型的方法信息: GetMethod() 和GetMethods() 。GetMethod() 方法返回System.Reflection.MethodInfo 對(duì)象的一個(gè)引用,其中包含一個(gè)方法的信息。GetMethods()返回這種引用的一個(gè)數(shù)組。其區(qū)別是GetMethods()返回所有方法的信息,而GetMethod()返回一個(gè)方法的信息,其中該方法包含特定的參數(shù)列表。這兩個(gè)方法都有重載方法,該重載方法有一個(gè)附加的參數(shù),即BindingFlags
36、枚舉值,表示應(yīng)返回哪些成員,例如,返回公有成員、實(shí)例成員和靜態(tài)成員等。例如,GetMethods()最簡(jiǎn)單的一個(gè)重載方法不帶參數(shù),返回?cái)?shù)據(jù)類型所有公共方法的信息:Type t = typeof(double);MethodInfo methods = t.GetMethods();foreach (MethodInfo nextMethod in methods)/ etc.Type 的成員方法如表12-3 所示遵循同一個(gè)模式。表 12-3返回的對(duì)象類型 方法(名稱為復(fù)數(shù)形式的方法返回一個(gè)數(shù)組)ConstructorInfo GetConstructor(), GetConstructors(
37、)EventInfo GetEvent(), GetEvents()FieldInfo GetField(), GetFields()InterfaceInfo GetInterface(), GetInterfaces()MemberInfo GetMember(), GetMembers()MethodInfo GetMethod(), GetMethods()PropertyInfo GetProperty(), GetProperties()GetMember()和GetMembers()方法返回?cái)?shù)據(jù)類型的一個(gè)或所有成員的信息,這些成員可以是構(gòu)造函數(shù)、屬性和方法等。最后要注意,可以調(diào)用
38、這些成員,其方式是調(diào)用Type的InvokeMember()方法,或者調(diào)用MethodInfo, PropertyInfo 和其他類的Invoke()方法。13.2.2 TypeView 示例下面用一個(gè)短小的示例TypeView 來(lái)說(shuō)明Type 類的一些功能,這個(gè)示例可以列出數(shù)據(jù)類331第部分 C# 語(yǔ)言型的所有成員。本例中主要介紹double 型的TypeView 用法,也可以修改該樣列中的一行代碼,使用其他的數(shù)據(jù)類型。TypeView 提供的信息要比在控制臺(tái)窗口中顯示的信息多得多,所以我們將打破常規(guī),在一個(gè)消息框中顯示這些信息。運(yùn)行double 型的TypeView 示例,結(jié)果如圖12-1
39、 所示。圖 12-1該消息框顯示了數(shù)據(jù)類型的名稱、全名和命名空間,以及底層類型和基類的名稱。然后迭代該數(shù)據(jù)類型的所有公有實(shí)例成員,顯示所聲明類型的每個(gè)成員、成員的類型(方法、字段等)以及成員的名稱。聲明類型是實(shí)際聲明類型成員的類名(換言之,如果在System.Double 中定義或重載,該聲明類型就是System.Double,如果成員繼承了某個(gè)基類,該聲明類就是相關(guān)基類的名稱)。TypeView 不會(huì)顯示方法的簽名,因?yàn)槲覀兪峭ㄟ^(guò)MemberInfo 對(duì)象獲取所有公有實(shí)例成員的信息,參數(shù)信息不能通過(guò)MemberInfo 對(duì)象來(lái)獲得。為了獲取該信息,需要引用MemberInfo 和其他更特殊的
40、對(duì)象,即需要分別獲取每一個(gè)成員類型的信息。TypeView 會(huì)顯示所有公有實(shí)例成員的信息,但對(duì)于double 來(lái)說(shuō),僅定義了字段和方法。把TypeView 編譯為一個(gè)控制臺(tái)應(yīng)用程序,可以在控制臺(tái)應(yīng)用程序中顯示消息框。但是,使用消息框就意味著需要引用基類程序集System.Windows.Forms. dll,它包含System.Windows.Forms 命名空間中的類, 在這個(gè)命名空間中, 定義了我們需要的MessageBox 類。下面列出TypeView 的代碼。開始時(shí)需要添加兩條using 語(yǔ)句:using System;using System.Text;using System.Wi
41、ndows.Forms;using System.Reflection;332第12 章反 射需要 System.Text 的原因是我們要使用StringBuilder 對(duì)象建立在消息框中顯示的文本,以及消息框本身的。全部代碼都放在類MainClass 中,這個(gè)類包含兩個(gè)靜態(tài)方法和一個(gè)靜態(tài)字段,StringBuilder 的一個(gè)實(shí)例叫作OutputText,用于創(chuàng)建在消息框中顯示的文本。Main 方法和類的聲明如下所示:class MainClassStatic StringBuilder OutputText = new StringBuilder();static void Main()/
42、 modify this line to retrieve details of any/ other data typeType t = typeof(double);AnalyzeType(t);MessageBox.Show(OutputText.ToString(), "Analysis of type "+ t.Name);Console.ReadLine();Main()方法首先聲明一個(gè)Type 對(duì)象,表示我們選擇的數(shù)據(jù)類型,再調(diào)用方法AnalyzeType(),從Type 對(duì)象中提取信息,并使用該信息建立輸出文本。最后在消息框中顯示輸出。使用MessageBo
43、x 類是非常直觀的:只需調(diào)用其靜態(tài)方法Show(),給它傳遞兩個(gè)字符串,分別為消息框中的文本和標(biāo)題。這些都由AnalyzeType()來(lái)完成:static void AnalyzeType(Type t)AddToOutput("Type Name: " + t.Name);AddToOutput("Full Name: " + t.FullName);AddToOutput("Namespace: " + t.Namespace);Type tBase = t.BaseType;if (tBase != null)AddToOutp
44、ut("Base Type:" + tBase.Name);Type tUnderlyingSystem = t.UnderlyingSystemType;if (tUnderlyingSystem != null)AddToOutput("UnderlyingSystem Type:" + tUnderlyingSystem.Name);AddToOutput("nPUBLIC MEMBERS:");MemberInfo Members = t.GetMembers();foreach (MemberInfo NextMember
45、in Members)333第部分 C# 語(yǔ)言AddToOutput(NextMember.DeclaringType + " " +NextMember.MemberType + " " + NextMember.Name);執(zhí)行這個(gè)方法,僅需調(diào)用Type 對(duì)象的各種屬性,就可以獲得我們需要的類型名稱的信息,再調(diào)用GetMembers()方法,獲得一個(gè)MemberInfo 對(duì)象數(shù)組,該數(shù)組用于顯示每個(gè)成員的信息。注意這里使用了一個(gè)輔助方法AddToOutput(),該方法創(chuàng)建要在消息框中顯示的文本:static void AddToOutput(str
46、ing Text)OutputText.Append("n" + Text);使用下面的命令編譯TypeView 程序集:csc /reference:System.Windows.Forms.dll TypeView.cs13.2.3 Assembly 類Assembly 類是在System.Reflection 命名空間中定義的,它允許訪問(wèn)給定程序集的元數(shù)據(jù),它也包含可以加載和執(zhí)行程序集(假定該程序集是可執(zhí)行的)的方法。與Type 類一樣,Assembly 類包含非常多的方法和屬性,這里不可能逐一論述。下面僅介紹完成示例WhatsNewAttributes 所需要的方法
47、和屬性。在使用Assembly 實(shí)例做一些工作前,需要把相應(yīng)的程序集加載到運(yùn)行進(jìn)程中。為此,可以使用靜態(tài)成員Assembly.Load()或Assembly.LoadFrom()。這兩個(gè)方法的區(qū)別是Load()的參數(shù)是程序集的名稱,運(yùn)行庫(kù)會(huì)在各個(gè)位置上搜索該程序集,這些位置包括本地目錄和全局程序集高速緩存。而LoadFrom()的參數(shù)是程序集的完整路徑名,不會(huì)在其他位置搜索該程序集:Assembly assembly1 = Assembly.Load("SomeAssembly");Assembly assembly2 = Assembly.LoadFrom("C
48、:My ProjectsSoftwareSomeOtherAssembly");這兩個(gè)方法都有許多其他重載,它們提供了其他安全信息。加載了一個(gè)程序集后,就可以使用它的各種屬性,例如查找它的全名:string name = assembly1.FullName;1. 查找在程序集中定義的類型Assembly 類的一個(gè)特性是可以獲得在相應(yīng)程序集中定義的所有類型的信息,只要調(diào)用Assembly.GetTypes()方法,就可以返回一個(gè)包含所有類型信息的System.Type 引用數(shù)組,然后就可以按照上一節(jié)的方式處理這些Type 引用了:334第12 章反 射Type types = the
49、Assembly.GetTypes();foreach(Type definedType in types)DoSomethingWith(definedType);2. 查找定制特性用于查找在程序集或類型中定義了什么定制特性的方法取決于與該特性相關(guān)的對(duì)象類型。如果要確定程序集中有什么定制特性,就需要調(diào)用Attribute 類的一個(gè)靜態(tài)方法GetCustomAttributes(),給它傳遞程序集的引用:Attribute definedAttributes =Attribute.GetCustomAttributes(assembly1);/ assembly1 is an Assembly
50、 object注意:這是相當(dāng)重要的。以前您可能想知道,在定義定制特性時(shí),必須為它們編寫類,為什么Microsoft 沒有更簡(jiǎn)單的語(yǔ)法。答案就在于此。定制特性與對(duì)象一樣,加載了程序集后,就可以讀取這些特性對(duì)象,查看它們的屬性,并且調(diào)用它們的方法。GetCustomAttributes()在用于獲取程序集的特性時(shí),有兩個(gè)重載方法:如果在調(diào)用它時(shí),除了程序集的引用外,沒有指定其他參數(shù),該方法就會(huì)返回為這個(gè)程序集定義的所有定制特性。當(dāng)然,也可以通過(guò)指定第二個(gè)參數(shù)來(lái)調(diào)用它,第二個(gè)參數(shù)表示特性類的一個(gè)Type對(duì)象,在這種情況下,GetCustomAttributes()就返回一個(gè)數(shù)組,該數(shù)組包含該特性類的
51、所有特性。注意,所有的特性都作為一般的Attribute 引用來(lái)獲取。如果要調(diào)用為定制特性定義的任何方法或?qū)傩裕?就需要把這些引用顯式轉(zhuǎn)換為相關(guān)的定制特性類。調(diào)用Assembly.GetCustomAttributes()的另一個(gè)重載方法,可以獲得與給定數(shù)據(jù)類型相關(guān)的定制特性信息,這次傳遞的是一個(gè)Type 引用,它描述了要獲取的任何相關(guān)特性的類型。另一方面,如果要獲得與方法、構(gòu)造函數(shù)和字段等相關(guān)的特性,就需要調(diào)用GetCustomAttributes()方法,該方法是類MethodInfo、 ConstructorInfo 和 FieldInfo 等的一個(gè)成員。如果只需要給定類型的一個(gè)特性,就
52、可以調(diào)用GetCustomAttribute()方法,它返回一個(gè)Attribute對(duì)象。在WhatsNewAttributes示例中使用GetCustomAttribute()方法,是為了確定程序集中是否有特性SupportsWhatsNew。為此,調(diào)用GetCustomAttributes(),傳遞對(duì)WhatsNewAttributes程序集的一個(gè)引用和SupportWhatsNewAttribute特性的類型。如果有這個(gè)特性,就返回一個(gè)Attribute實(shí)例。如果在程序集中沒有定義任何實(shí)例,就返回null。如果找到兩個(gè)或多個(gè)實(shí)例, GetCustomAttribute() 方法就拋出一個(gè)異
53、常System.Reflection.AmbiguousMatchException:Attribute supportsAttribute =Attribute.GetCustomAttributes(assembly1,typeof(SupportsWhats NewAttribute);335第部分 C# 語(yǔ)言13.2.4 完成WhatsNewAttributes 示例現(xiàn)在已經(jīng)有足夠的知識(shí)來(lái)完成WhatsNewAttributes 示例了。為該示例中的最后一個(gè)程序集LookUpWhatsNew 編寫源代碼,這部分應(yīng)用程序是一個(gè)控制臺(tái)應(yīng)用程序,它需要引用其他兩個(gè)程序集WhatsNewAtt
54、ributes 和VectorClass。這是一個(gè)命令行應(yīng)用程序,但仍可以象前面的TypeView 示例那樣在消息框中顯示結(jié)果,因?yàn)榻Y(jié)果是許多文本,所以不能顯示在一個(gè)控制臺(tái)窗口屏幕上。這個(gè)文件的名稱為L(zhǎng)ookUpWhatsNew.cs,編譯它的命令是:csc /reference:WhatsNewAttributes.dll /reference:VectorClass.dllLookUpWhatsNew.cs在這個(gè)文件的源代碼中,首先指定要使用的命名空間System.Text,因?yàn)樾枰褂靡粋€(gè)StringBuilder 對(duì)象:using System;using System.Reflect
55、ion;using System.Windows.Forms;using System.Text;using Wrox.ProCSharp.VectorClass;using Wrox.ProCSharp.WhatsNewAttributes;類WhatsNewChecker 包含主程序入口和其他方法。我們定義的所有方法都在這個(gè)類中,它還有兩個(gè)靜態(tài)字段:outputText 和backDateTo。outputText 包含在準(zhǔn)備階段創(chuàng)建的文本,這個(gè)文本要寫到消息框中,backDateTo 存儲(chǔ)了選擇的日期自從該日期以來(lái)的所有修改都要顯示出來(lái)。一般情況下,需要顯示一個(gè)對(duì)話框,讓用戶選擇這個(gè)日期
56、,但我們不想編寫這段代碼,以免轉(zhuǎn)移讀者的注意力。因此,把backDateTo 硬編碼為日期2007 年2月1 日。在下載這段代碼時(shí),很容易修改這個(gè)日期:class WhatsNewCheckerstatic StringBuilder outputText = new StringBuilder(1000);static DateTime backDateTo = new DateTime(2007, 2, 1);static void Main()Assembly theAssembly = Assembly.Load("VectorClass");Attribute supportsAttribute =Attribute.GetCustomAttribute(theAssembly, typeof(SupportsWhatsNewAttribute);string Name = theAssembly.FullName;AddToMessage("Assembly: " + Name);if (suppo
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 紙盒包裝設(shè)計(jì)核心要素與流程
- 外資企業(yè)的中級(jí)經(jīng)濟(jì)師試題及答案
- 經(jīng)濟(jì)法概論理論基礎(chǔ)試題及答案
- 項(xiàng)目溝通的渠道與方式考核試題及答案
- 行政管理公共關(guān)系學(xué)行業(yè)分析試題及答案
- 水利水電工程學(xué)科交叉與融合試題及答案
- 行政管理中的組織管理試題及答案
- 關(guān)聯(lián)知識(shí)的市政工程試題及答案
- 2025年中級(jí)經(jīng)濟(jì)師提升學(xué)習(xí)效率的試題及答案
- 農(nóng)業(yè)經(jīng)濟(jì)管理體系建設(shè)與實(shí)施方案合同
- 消毒供應(yīng)中心手工清洗操作流程
- 發(fā)電量管理考核辦法
- 骨科常用藥物相關(guān)知識(shí)
- 2022級(jí)中餐烹飪(烹飪工藝與營(yíng)養(yǎng)) 專業(yè)校企合作人才培養(yǎng)方案(五年制)
- 2025年音樂(lè)節(jié)演唱會(huì)明星藝人歌手樂(lè)隊(duì)演出場(chǎng)費(fèi)價(jià)格表
- 青年紅色筑夢(mèng)之旅創(chuàng)業(yè)計(jì)劃
- 2025年人工智能工程師專業(yè)知識(shí)考核試卷:人工智能在語(yǔ)音識(shí)別中的應(yīng)用試題
- 12.2.1.2+用條形圖和折線圖描述數(shù)據(jù)教案+2024-2025學(xué)年人教版數(shù)學(xué)七年級(jí)下冊(cè)
- 學(xué)校內(nèi)控制度及手冊(cè)
- 新蘇教版一年級(jí)數(shù)學(xué)下冊(cè)第七單元第1課時(shí)《觀察物體(1)》課件
- 新版《醫(yī)療器械經(jīng)營(yíng)質(zhì)量管理規(guī)范》(2024)培訓(xùn)試題及答案
評(píng)論
0/150
提交評(píng)論