反射 .
提綱:
1、 什么是反射
2、 命名空間與裝配件的關(guān)系
3、 運(yùn)行期得到類型信息有什么用
4、 如何使用反射獲取類型
5、 如何根據(jù)類型來動(dòng)態(tài)創(chuàng)建對象
6、 如何獲取方法以及動(dòng)態(tài)調(diào)用方法
7、 動(dòng)態(tài)創(chuàng)建委托
?
1、什么是反射
??????? Reflection,中文翻譯為反射。
??????? 這是.Net中獲取運(yùn)行時(shí)類型信息的方式,.Net的應(yīng)用程序由幾個(gè)部分:‘程序集(Assembly)’、‘模塊(Module)’、‘類型(class)’組成,而反射提供一種編程的方式,讓程序員可以在程序運(yùn)行期獲得這幾個(gè)組成部分的相關(guān)信息,例如:
??????? Assembly類可以獲得正在運(yùn)行的裝配件信息,也可以動(dòng)態(tài)的加載裝配件,以及在裝配件中查找類型信息,并創(chuàng)建該類型的實(shí)例。
Type類可以獲得對象的類型信息,此信息包含對象的所有要素:方法、構(gòu)造器、屬性等等,通過Type類可以得到這些要素的信息,并且調(diào)用之。
MethodInfo包含方法的信息,通過這個(gè)類可以得到方法的名稱、參數(shù)、返回值等,并且可以調(diào)用之。
諸如此類,還有FieldInfo、EventInfo等等,這些類都包含在System.Reflection命名空間下。
?
?反射的定義:審查元數(shù)據(jù)并收集關(guān)于它的類型信息的能力。元數(shù)據(jù)(編譯以后的最基本數(shù)據(jù)單元)就是一大堆的表,當(dāng)編譯程序集或者模塊時(shí),編譯器會創(chuàng)建一個(gè)類定義表,一個(gè)字段定義表,和一個(gè)方法定義表等,。System.reflection命名空間包含的幾個(gè)類,允許你反射(解析)這些元數(shù)據(jù)表的代碼
2、命名空間與裝配件的關(guān)系
?
?和反射相關(guān)的命名空間(我們就是通過這幾個(gè)命名空間訪問反射信息):
??
??System.Reflection.MemberInfo
??
?? System.Reflection.EventInfo
??
?? System.Reflection.FieldInfo
??
?? System.Reflection.MethodBase
??
?? System.Reflection.ConstructorInfo
??
?? System.Reflection.MethodInfo
??
?? System.Reflection.PropertyInfo
??
?? System.Type
??
?? System.Reflection.Assembly
??????? 很多人對這個(gè)概念可能還是很不清晰,對于合格的.Net程序員,有必要對這點(diǎn)進(jìn)行澄清。
??????? 命名空間類似與Java的包,但又不完全等同,因?yàn)镴ava的包必須按照目錄結(jié)構(gòu)來放置,命名空間則不需要。
??????? 裝配件是.Net應(yīng)用程序執(zhí)行的最小單位,編譯出來的.dll、.exe都是裝配件。
??????? 裝配件和命名空間的關(guān)系不是一一對應(yīng),也不互相包含,一個(gè)裝配件里面可以有多個(gè)命名空間,一個(gè)命名空間也可以在多個(gè)裝配件中存在,這樣說可能有點(diǎn)模糊,舉個(gè)例子:
裝配件A:
namespace?? N1
{
????? public?? class?? AC1?? {…}
????? public?? class?? AC2?? {…}
}
namespace?? N2
{
????? public?? class?? AC3?? {…}
????? public?? class?? AC4{…}
}
裝配件B:
namespace?? N1
{
????? public?? class?? BC1?? {…}
????? public?? class?? BC2?? {…}
}
namespace?? N2
{
????? public?? class?? BC3?? {…}
????? public?? class?? BC4{…}
}
??????? 這兩個(gè)裝配件中都有N1和N2兩個(gè)命名空間,而且各聲明了兩個(gè)類,這樣是完全可以的,然后我們在一個(gè)應(yīng)用程序中引用裝配件A,那么在這個(gè)應(yīng)用程序中,我們能看到N1下面的類為AC1和AC2,N2下面的類為AC3和AC4。
??????? 接著我們?nèi)サ魧的引用,加上對B的引用,那么我們在這個(gè)應(yīng)用程序下能看到的N1下面的類變成了BC1和BC2,N2下面也一樣。
??????? 如果我們同時(shí)引用這兩個(gè)裝配件,那么N1下面我們就能看到四個(gè)類:AC1、AC2、BC1和BC2。
??????? 到這里,我們可以清楚一個(gè)概念了,命名空間只是說明一個(gè)類型是那個(gè)族的,比如有人是漢族、有人是回族;而裝配件表明一個(gè)類型住在哪里,比如有人住在北京、有人住在上海;那么北京有漢族人,也有回族人,上海有漢族人,也有回族人,這是不矛盾的。
??????? 上面我們說了,裝配件是一個(gè)類型居住的地方,那么在一個(gè)程序中要使用一個(gè)類,就必須告訴編譯器這個(gè)類住在哪兒,編譯器才能找到它,也就是說必須引用該裝配件。
??????? 那么如果在編寫程序的時(shí)候,也許不確定這個(gè)類在哪里,僅僅只是知道它的名稱,就不能使用了嗎?答案是可以,這就是反射了,就是在程序運(yùn)行的時(shí)候提供該類型的地址,而去找到它。
有興趣的話,接著往下看吧。
3、運(yùn)行期得到類型信息有什么用
?
?反射的作用:
??
??1. 可以使用反射動(dòng)態(tài)地創(chuàng)建類型的實(shí)例,將類型綁定到現(xiàn)有對象,或從現(xiàn) 有對象中獲取類型
??
??2. 應(yīng)用程序需要在運(yùn)行時(shí)從某個(gè)特定的程序集中載入一個(gè)特定的類型,以便實(shí)現(xiàn)某個(gè)任務(wù)時(shí)可以用到反射。
??
??3. 反射主要應(yīng)用與類庫,這些類庫需要知道一個(gè)類型的定義,以便提供更多的功能。
??????? 有人也許疑問,既然在開發(fā)時(shí)就能夠?qū)懞么a,干嘛還放到運(yùn)行期去做,不光繁瑣,而且效率也受影響。
這就是個(gè)見仁見智的問題了,就跟早綁定和晚綁定一樣,應(yīng)用到不同的場合。有的人反對晚綁定,理由是損耗效率,但是很多人在享受虛函數(shù)帶來的好處的時(shí)侯還沒有意識到他已經(jīng)用上了晚綁定。這個(gè)問題說開去,不是三言兩語能講清楚的,所以就點(diǎn)到為止了。
??????? 我的看法是,晚綁定能夠帶來很多設(shè)計(jì)上的便利,合適的使用能夠大大提高程序的復(fù)用性和靈活性,但是任何東西都有兩面性,使用的時(shí)侯,需要再三衡量。
接著說,運(yùn)行期得到類型信息到底有什么用呢?
還是舉個(gè)例子來說明,很多軟件開發(fā)者喜歡在自己的軟件中留下一些接口,其他人可以編寫一些插件來擴(kuò)充軟件的功能,比如我有一個(gè)媒體播放器,我希望以后可以很方便的擴(kuò)展識別的格式,那么我聲明一個(gè)接口:
public?? interface?? IMediaFormat
{
string?? Extension?? {get;}
Decoder?? GetDecoder();
}
這個(gè)接口中包含一個(gè)Extension屬性,這個(gè)屬性返回支持的擴(kuò)展名,另一個(gè)方法返回一個(gè)解碼器的對象(這里我假設(shè)了一個(gè)Decoder的類,這個(gè)類提供把文件流解碼的功能,擴(kuò)展插件可以派生之),通過解碼器對象我就可以解釋文件流。
那么我規(guī)定所有的解碼插件都必須派生一個(gè)解碼器,并且實(shí)現(xiàn)這個(gè)接口,在GetDecoder方法中返回解碼器對象,并且將其類型的名稱配置到我的配置文件里面。
這樣的話,我就不需要在開發(fā)播放器的時(shí)侯知道將來擴(kuò)展的格式的類型,只需要從配置文件中獲取現(xiàn)在所有解碼器的類型名稱,而動(dòng)態(tài)的創(chuàng)建媒體格式的對象,將其轉(zhuǎn)換為IMediaFormat接口來使用。
這就是一個(gè)反射的典型應(yīng)用。
4、如何使用反射獲取類型
?
?
應(yīng)用要點(diǎn):
??
??1. 現(xiàn)實(shí)應(yīng)用程序中很少有應(yīng)用程序需要使用反射類型
??
??2. 使用反射動(dòng)態(tài)綁定需要犧牲性能
??
??3. 有些元數(shù)據(jù)信息是不能通過反射獲取的
??
??4. 某些反射類型是專門為那些clr 開發(fā)編譯器的開發(fā)使用的,所以你要意識到不是所有的反射類型都是適合每個(gè)人的。
??????? 首先我們來看如何獲得類型信息。
??????? 獲得類型信息有兩種方法,一種是得到實(shí)例對象
??????? 這個(gè)時(shí)侯我僅僅是得到這個(gè)實(shí)例對象,得到的方式也許是一個(gè)object的引用,也許是一個(gè)接口的引用,但是我并不知道它的確切類型,我需要了解,那么就可以通過調(diào)用System.Object上聲明的方法GetType來獲取實(shí)例對象的類型對象,比如在某個(gè)方法內(nèi),我需要判斷傳遞進(jìn)來的參數(shù)是否實(shí)現(xiàn)了某個(gè)接口,如果實(shí)現(xiàn)了,則調(diào)用該接口的一個(gè)方法:
…
public?? void?? Process(?? object?? processObj?? )
{
Type?? t?? =?? processsObj.GetType();
if(?? t.GetInterface(“ITest”)?? !=null?? )
??????????????????? …
}
…
??????? 另外一種獲取類型的方法是通過Type.GetType以及Assembly.GetType方法,如:
????????????? Type?? t?? =?? Type.GetType(“System.String”);
??????? 需要注意的是,前面我們講到了命名空間和裝配件的關(guān)系,要查找一個(gè)類,必須指定它所在的裝配件,或者在已經(jīng)獲得的Assembly實(shí)例上面調(diào)用GetType。
??????? 本裝配件中類型可以只寫類型名稱,另一個(gè)例外是mscorlib.dll,這個(gè)裝配件中聲明的類型也可以省略裝配件名稱(.Net裝配件編譯的時(shí)候,默認(rèn)都引用了mscorlib.dll,除非在編譯的時(shí)候明確指定不引用它),比如:
????????? System.String是在mscorlib.dll中聲明的,上面的Type?? t?? =?? Type.GetType(“System.String”)是正確的
????????? System.Data.DataTable是在System.Data.dll中聲明的,那么:
Type.GetType(“System.Data.DataTable”)就只能得到空引用。
????????? 必須:
Type?? t?? =?? Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,?? Culture=neutral,?? PublicKeyToken=b77a5c561934e089");
????????????????5、如何根據(jù)類型來動(dòng)態(tài)創(chuàng)建對象
??????? System.Activator提供了方法來根據(jù)類型動(dòng)態(tài)創(chuàng)建對象,比如創(chuàng)建一個(gè)DataTable:
Type?? t?? =?? Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,?? Culture=neutral,?? PublicKeyToken=b77a5c561934e089");
DataTable?? table?? =?? (DataTable)Activator.CreateInstance(t);
??? 例二:根據(jù)有參數(shù)的構(gòu)造器創(chuàng)建對象
namespace?? TestSpace?? {
public?? class?? TestClass
{
????? private?? string?? _value;
????? public?? TestClass(string?? value)?? {
_value=value;
????? }
}
}
…
Type?? t?? =?? Type.GetType(“TestSpace.TestClass”);
Object[]?? constructParms?? =?? new?? object[]?? {“hello”};?? //構(gòu)造器參數(shù)
TestClass?? obj?? =?? (TestClass)Activator.CreateInstance(t,constructParms);
…
把參數(shù)按照順序放入一個(gè)Object數(shù)組中即可
6、如何獲取方法以及動(dòng)態(tài)調(diào)用方法
namespace?? TestSpace
{
????? public?? class?? TestClass?? {
????????? private?? string?? _value;
????????? public?? TestClass()?? {
????????? }
????????? public?? TestClass(string?? value)?? {
_value?? =?? value;
????????? }
????????? public?? string?? GetValue(?? string?? prefix?? )?? {
if(?? _value==null?? )
??????? return?? "NULL";
else
??????? return?? prefix+"?? :?? "+_value;
??????????? }
??????????? public?? string?? Value?? {
set?? {
_value=value;
}
get?? {
if(?? _value==null?? )
return?? "NULL";
else
return?? _value;
}
??????????? }
????? }
}
??????? 上面是一個(gè)簡單的類,包含一個(gè)有參數(shù)的構(gòu)造器,一個(gè)GetValue的方法,一個(gè)Value屬性,我們可以通過方法的名稱來得到方法并且調(diào)用之,如:
//獲取類型信息
Type?? t?? =?? Type.GetType("TestSpace.TestClass");
//構(gòu)造器的參數(shù)
object[]?? constuctParms?? =?? new?? object[]{"timmy"};
//根據(jù)類型創(chuàng)建對象
object?? dObj?? =?? Activator.CreateInstance(t,constuctParms);
//獲取方法的信息
MethodInfo?? method?? =?? t.GetMethod("GetValue");
//調(diào)用方法的一些標(biāo)志位,這里的含義是Public并且是實(shí)例方法,這也是默認(rèn)的值
BindingFlags?? flag?? =?? BindingFlags.Public?? |?? BindingFlags.Instance;
//GetValue方法的參數(shù)
object[]?? parameters?? =?? new?? object[]{"Hello"};
//調(diào)用方法,用一個(gè)object接收返回值
object?? returnValue?? =?? method.Invoke(dObj,flag,Type.DefaultBinder,parameters,null);
??????? 屬性與方法的調(diào)用大同小異,大家也可以參考MSDN
7、動(dòng)態(tài)創(chuàng)建委托
??????? 委托是C#中實(shí)現(xiàn)事件的基礎(chǔ),有時(shí)候不可避免的要?jiǎng)討B(tài)的創(chuàng)建委托,實(shí)際上委托也是一種類型:System.Delegate,所有的委托都是從這個(gè)類派生的
??????? System.Delegate提供了一些靜態(tài)方法來動(dòng)態(tài)創(chuàng)建一個(gè)委托,比如一個(gè)委托:
namespace?? TestSpace?? {
????? delegate?? string?? TestDelegate(string?? value);
????? public?? class?? TestClass?? {
public?? TestClass()?? {
????????????????? }
????????????????? public?? void?? GetValue(string?? value)?? {
????????????????????????? return?? value;
????????????????? }
??????? }
}
使用示例:
TestClass?? obj?? =?? new?? TestClass();
//獲取類型,實(shí)際上這里也可以直接用typeof來獲取類型
Type?? t?? =?? Type.GetType(“TestSpace.TestClass”);
//創(chuàng)建代理,傳入類型、創(chuàng)建代理的對象以及方法名稱
TestDelegate?? method?? =?? (TestDelegate)Delegate.CreateDelegate(t,obj,”GetValue”);
String?? returnValue?? =?? method(“hello”);
?
反射appDomain 的程序集
??
?? 當(dāng)你需要反射AppDomain 中包含的所有程序集,示例如下:
?? static void Main
??
?? {
??
?? //通過GetAssemblies 調(diào)用appDomain的所有程序集
??
??foreach (Assembly assem in Appdomain.currentDomain.GetAssemblies())
??
??{
??
?? //反射當(dāng)前程序集的信息
??
?? reflector.ReflectOnAssembly(assem)
??
??}
??
??}
??
??說明:調(diào)用AppDomain 對象的GetAssemblies 方法 將返回一個(gè)由System.Reflection.Assembly元素組成的數(shù)組。
??
??反射單個(gè)程序集
??
??上面的方法講的是反射AppDomain的所有程序集,我們可以顯示的調(diào)用其中的一個(gè)程序集,system.reflecton.assembly 類型提供了下面三種方法:
??
??1. Load 方法:極力推薦的一種方法,Load 方法帶有一個(gè)程序集標(biāo)志并載入它,Load 將引起CLR把策略應(yīng)用到程序集上,先后在全局程序集緩沖區(qū),應(yīng)用程序基目錄和私有路徑下面查找該程序集,如果找不到該程序集系統(tǒng)拋出異常
??
??2. LoadFrom 方法:傳遞一個(gè)程序集文件的路徑名(包括擴(kuò)展名),CLR會載入您指定的這個(gè)程序集,傳遞的這個(gè)參數(shù)不能包含任何關(guān)于版本號的信息,區(qū)域性,和公鑰信息,如果在指定路徑找不到程序集拋出異常。
??
??3. LoadWithPartialName:永遠(yuǎn)不要使用這個(gè)方法,因?yàn)閼?yīng)用程序不能確定再在載入的程序集的版本。該方法的唯一用途是幫助那些在.Net框架的測試環(huán)節(jié)使用.net 框架提供的某種行為的客戶,這個(gè)方法將最終被拋棄不用。
??
??注意:system.AppDomain 也提供了一種Load 方法,他和Assembly的靜態(tài)Load 方法不一樣,AppDomain的load 方法是一種實(shí)例方法,返回的是一個(gè)對程序集的引用,Assembly的靜態(tài)Load 方發(fā)將程序集按值封裝發(fā)回給發(fā)出調(diào)用的AppDomain.盡量避免使用AppDomain的load 方法
??
??
??
??利用反射獲取類型信息
??
??前面講完了關(guān)于程序集的反射,下面在講一下反射層次模型中的第三個(gè)層次,類型反射
??
??一個(gè)簡單的利用反射獲取類型信息的例子:
??
??using system;
??
??using sytem.reflection;
??
??class reflecting
??
??{
??
?? static void Main(string[]args)
??
??{
??
?? reflecting reflect=new reflecting();//定義一個(gè)新的自身類
??
?? //調(diào)用一個(gè)reflecting.exe程序集
??
?? assembly myAssembly =assembly.loadfrom(“reflecting.exe”)
??
?? reflect.getreflectioninfo(myAssembly);//獲取反射信息
??
??}
??
??//定義一個(gè)獲取反射內(nèi)容的方法
??
??void getreflectioninfo(assembly myassembly)
??
??{
??
?? type[] typearr=myassemby.Gettypes();//獲取類型
??
?? foreach (type type in typearr)//針對每個(gè)類型獲取詳細(xì)信息
??
?? {
??
?? //獲取類型的結(jié)構(gòu)信息
??
?? constructorinfo[] myconstructors=type.GetConstructors;
??
?? //獲取類型的字段信息
??
?? fieldinfo[] myfields=type.GetFiedls()
??
?? //獲取方法信息
??
?? MethodInfo myMethodInfo=type.GetMethods();
??
?? //獲取屬性信息
??
?? propertyInfo[] myproperties=type.GetProperties
??
?? //獲取事件信息
??
?? EventInfo[] Myevents=type.GetEvents;
??
??
??
??}
??
??}
??
??}
??
??其它幾種獲取type對象的方法:
??
??1. System.type 參數(shù)為字符串類型,該字符串必須指定類型的完整名稱(包括其命名空間)
??
??2. System.type 提供了兩個(gè)實(shí)例方法:GetNestedType,GetNestedTypes
??
??3. Syetem.Reflection.Assembly 類型提供的實(shí)例方法是:GetType,GetTypes,GetExporedTypes
??
??4. System.Reflection.Moudle 提供了這些實(shí)例方法:GetType,GetTypes,FindTypes
??
??設(shè)置反射類型的成員
??
?? 反射類型的成員就是反射層次模型中最下面的一層數(shù)據(jù)。我們可以通過type對象的GetMembers 方法取得一個(gè)類型的成員。如果我們使用的是不帶參數(shù)的GetMembers,它只返回該類型的公共定義的靜態(tài)變量和實(shí)例成員,我們也可以通過使用帶參數(shù)的GetMembers通過參數(shù)設(shè)置來返回指定的類型成員。具體參數(shù)參考msdn 中system.reflection.bindingflags 枚舉類型的詳細(xì)說明。
??
??例如:
??
??
??
??//設(shè)置需要返回的類型的成員內(nèi)容
??
??bindingFlags bf=bingdingFlags.DeclaredOnly|bingdingFlags.Nonpublic|BingdingFlags.Public;
??
??foreach (MemberInfo mi int t.getmembers(bf))
??
??{
??
?? writeline(mi.membertype) //輸出指定的類型成員
??
??}
??
??通過反射創(chuàng)建類型的實(shí)例
??
??通過反射可以獲取程序集的類型,我們就可以根據(jù)獲得的程序集類型來創(chuàng)建該類型新的實(shí)例,這也是前面提到的在運(yùn)行時(shí)創(chuàng)建對象實(shí)現(xiàn)晚綁定的功能
??
??我們可以通過下面的幾個(gè)方法實(shí)現(xiàn):
??
??1. System.Activator 的CreateInstance方法。該方法返回新對象的引用。具體使用方法參見msnd
??
??2. System.Activator 的createInstanceFrom 與上一個(gè)方法類似,不過需要指定類型及其程序集
??
??3. System.Appdomain 的方法:createInstance,CreateInstanceAndUnwrap,CreateInstranceFrom和CreateInstraceFromAndUnwrap
??
??4. System.type的InvokeMember實(shí)例方法:這個(gè)方法返回一個(gè)與傳入?yún)?shù)相符的構(gòu)造函數(shù),并構(gòu)造該類型。
??
??5. System.reflection.constructinfo 的Invoke實(shí)例方法
??
??反射類型的接口
??
??如果你想要獲得一個(gè)類型繼承的所有接口集合,可以調(diào)用Type的FindInterfaces GetInterface或者GetInterfaces。所有這些方法只能返回該類型直接繼承的接口,他們不會返回從一個(gè)接口繼承下來的接口。要想返回接口的基礎(chǔ)接口必須再次調(diào)用上述方法。
??
??反射的性能:
??
??使用反射來調(diào)用類型或者觸發(fā)方法,或者訪問一個(gè)字段或者屬性時(shí)clr 需 要做更多的工作:校驗(yàn)參數(shù),檢查權(quán)限等等,所以速度是非常慢的。所以盡量不要使用反射進(jìn)行編程,對于打算編寫一個(gè)動(dòng)態(tài)構(gòu)造類型(晚綁定)的應(yīng)用程序,可以采取以下的幾種方式進(jìn)行代替:
??
??1. 通過類的繼承關(guān)系。讓該類型從一個(gè)編譯時(shí)可知的基礎(chǔ)類型派生出來,在運(yùn)行時(shí)生成該類 型的一個(gè)實(shí)例,將對其的引用放到其基礎(chǔ)類型的一個(gè)變量中,然后調(diào)用該基礎(chǔ)類型的虛方法。
??
??2. 通過接口實(shí)現(xiàn)。在運(yùn)行時(shí),構(gòu)建該類型的一個(gè)實(shí)例,將對其的引用放到其接口類型的一個(gè)變量中,然后調(diào)用該接口定義的虛方法。
??
??3.通過委托實(shí)現(xiàn)。讓該類型實(shí)現(xiàn)一個(gè)方法,其名稱和原型都與一個(gè)在編譯時(shí)就已知的委托相符。在運(yùn)行時(shí)先構(gòu)造該類型的實(shí)例,然后在用該方法的對象及名稱構(gòu)造出該委托的實(shí)例,接著通過委托調(diào)用你想要的方法。這個(gè)方法相對與前面兩個(gè)方法所作的工作要多一些,效率更低一些
?
再比較動(dòng)態(tài)調(diào)用代碼 上次在MSDN網(wǎng)站看到一個(gè)比較動(dòng)態(tài)調(diào)用代碼的文章,用到的例子似乎比較復(fù)雜,為計(jì)算一個(gè)復(fù)雜多項(xiàng)式子而將其中部分割開,動(dòng)態(tài)形成代碼段來被循環(huán)調(diào)用。詳細(xì)看.NET下幾種動(dòng)態(tài)生成代碼方式比較。今天看到微軟C#團(tuán)隊(duì)的Eric Gunnerson寫的另外一篇關(guān)于動(dòng)態(tài)調(diào)用代碼性能的比較文章,為了說明結(jié)果和計(jì)算的準(zhǔn)確性,減少由于函數(shù)復(fù)雜而受編譯優(yōu)化的影響,他使用了一個(gè)極為簡單的例子:輸入一個(gè)參數(shù),然后返回這個(gè)參數(shù)加一,這么簡單的函數(shù),優(yōu)化和沒有優(yōu)化的代碼應(yīng)該不會有差別的了。
?
????public?class?Processor????{
????????public?int?Process(int?value)
????????{
????????????return?value?+?1;
????????}
????}
?
而對比方面,除了上次那幾種外,還加了代理方式調(diào)用來進(jìn)行比較。
1. 直接調(diào)用
?
int?value?=?processor.Process(i);?
2. 用反射機(jī)制,Type.InvokeMember()調(diào)用。?
????Type?t?=?typeof(Processor);????int?value?=?
????????(int)?t.InvokeMember(
??????????????????"Process",?
?????????BindingFlags.Instance?|?BindingFlags.Public?|?
??????????????????BindingFlags.InvokeMethod,?
??????????????????null,?processor,?new?object[]?{i});
?
3. 通過一個(gè)接口?
????public?interface?IProcessor???{
????????int?Process(int?value);
????}
?
4. 通過一個(gè)委托Delegate?
????public?delegate?int?ProcessCaller(int?value);????ProcessCaller?processCaller?=?new?ProcessCaller(processor.Process);
????int?value?=?processCaller(i);?
?
5. 也通過反射機(jī)制建立委托再動(dòng)態(tài)調(diào)用?
????Type?delegateType?=?CreateCustomDelegate(methodInfo);????Delegate?p?=?Delegate.CreateDelegate(delegateType,?
?????????????????????????????????????????process,?"Process");
????int?value?=?(int)?p.DynamicInvoke(new?object[]?{i});
?
6. 元編程方式 對于2和5由于使用反射機(jī)制,不可避免需要建立中間的臨時(shí)對象去傳遞參數(shù),將參數(shù)和返回值裝箱等操作,因此花費(fèi)了大量的機(jī)器時(shí)間。下面是運(yùn)行的某次結(jié)果(循環(huán)100000次):
?
結(jié)論:
1.直接調(diào)用速度最快是肯定的。
2.接口調(diào)用比元編程速度快,而元編程又比委托方式快,但微軟相信Whidbey會極大優(yōu)化委托調(diào)用方式,從而使它接近接口調(diào)用的水平。
3.直接用Type的反射機(jī)制是速度最慢的,比用反射機(jī)制建立委托來動(dòng)態(tài)調(diào)用還慢。
4.直接使用委托不夠靈活,有時(shí)候需要用反射機(jī)制建立委托來調(diào)用,但會減低性能,希望Whidbey優(yōu)化了委托的性能后這種情況可以改善,靈活是需要犧牲性能的。 相關(guān)代碼 CN.Text開發(fā)筆記—利用反射將數(shù)據(jù)讀入實(shí)體類 在實(shí)際開發(fā)中,我們經(jīng)常需要從數(shù)據(jù)庫中讀取數(shù)據(jù)并賦值給實(shí)體類的相應(yīng)屬性。在.Text的DataDTOProvider中存在大量這樣的代碼, 比如:
public?Role[]?GetRoles(int?BlogID)
????????{
????????????System.Collections.ArrayList?al=new?System.Collections.ArrayList();
????????????IDataReader?reader=DbProvider.Instance().GetRoles(BlogID);
????????????try
????????????{
????????????????while(reader.Read())
????????????????{
????????????????????Role?role=new?Role();
????????????????????if(reader["RoleID"]!=DBNull.Value)
????????????????????{
????????????????????????role.RoleID=(int)reader["RoleID"];
????????????????????}
????????????????????if(reader["Name"]!=DBNull.Value)
????????????????????{
????????????????????????role.Name=(string)reader["Name"];
????????????????????}
????????????????????if(reader["Description"]!=DBNull.Value)
????????????????????{
????????????????????????role.Description=(string)reader["Description"];
????????????????????}
????????????????????//ReaderToObject(reader,role);
????????????????????al.Add(role);
????????????????}
????????????}
????????????finally
????????????{
????????????????reader.Close();
????????????}
????????????return?(Role[])al.ToArray(typeof(Role));
????????
????????}
對于上面的代碼,我覺得有幾點(diǎn)不優(yōu)雅之處:
1、每次對Role的屬性進(jìn)行賦值時(shí),都要檢查reader的值是否為DBNull,出現(xiàn)了很多重復(fù)代碼
2、每次對Role的屬性進(jìn)行賦值時(shí),都要進(jìn)行類型轉(zhuǎn)換, 而Role屬性的類型是已知的,是不是可以自動(dòng)完成這樣的轉(zhuǎn)換?
3、每次對Role的屬性進(jìn)行賦值時(shí),都要進(jìn)行Role屬性與數(shù)據(jù)庫字段的對應(yīng)。如果我們在設(shè)計(jì)數(shù)據(jù)庫與實(shí)體類時(shí),保證數(shù)據(jù)庫字段與實(shí)體類屬性采用同樣的名稱,那利用反射,我們可以通過代碼自動(dòng)進(jìn)行屬性與字段的對應(yīng)。即使數(shù)據(jù)庫字段與屬性不同名,我們也可以通過更改查詢語句,來做到這一點(diǎn)。
是不是可以對上面的代碼進(jìn)行改進(jìn),使代碼變得更優(yōu)雅?那優(yōu)雅的代碼應(yīng)該是什么樣的呢?如果我們用上面代碼中注釋的代碼行ReaderToObject(reader,role);取代它之前的對Role屬性進(jìn)行賦值的語句,是不是會使代碼變得更優(yōu)雅?ReaderToObject的作用就是自動(dòng)完成將reader中的值寫入到role中對應(yīng)的屬性中(前提是reader中的字段與role中對應(yīng)的屬性具有相同的名稱)。現(xiàn)在我們的任務(wù)就是實(shí)現(xiàn)ReaderToObject, 有了強(qiáng)大的武器—Reflection,我們的任務(wù)就變得很輕松, 也不多說了,下面的代碼是我的實(shí)現(xiàn)方法: private?void?ReaderToObject(IDataReader?reader,object?targetObj)
????????{
????????????for(int?i=0;i<reader.FieldCount;i++)
????????????{
????????????????System.Reflection.PropertyInfo?propertyInfo=targetObj.GetType().GetProperty(reader.GetName(i));
????????????????if(propertyInfo!=null)
????????????????{
????????????????????if(reader.GetValue(i)!=DBNull.Value)
????????????????????{
????????????????????????propertyInfo.SetValue(targetObj,reader.GetValue(i),null);
????????????????????}
????????????????}
????????????}
????????} ReaderToObject可以將reader中的數(shù)據(jù)讀入到任何實(shí)體類中。數(shù)據(jù)庫字段與實(shí)體類屬性的映射原則是名稱相同。當(dāng)然,我們也可以通過配置文件來進(jìn)行兩者映射。
?先來看這段NUnit測試代碼,我們希望用反射機(jī)制在運(yùn)行時(shí)訪問一個(gè)對象的枚舉類型的域或?qū)傩?#xff1a; [TestFixture]
public class PaymentInfo
{
?public enum PaymentType
?{
Cash, CreditCard, Check
?}
?public PaymentType Type;
?public void Test()
?{
PaymentInfo payment = new PaymentInfo();
payment.Type = PaymentType.Cash;
System.Reflection.FieldInfo enumField = GetType().GetField("Type");
int paymentTypeInt32;
paymentTypeInt32 = (int)enumField.GetValue(payment);
Assert.AreEqual((int)PaymentType.Cash, paymentTypeInt32);
??? enumField.SetValue(payment, paymentTypeInt32);
Assert.AreEqual(PaymentType.Cash, payment.Type);
?}
} 在這個(gè)測試中,使之通過的辦法其實(shí)非常簡單:把劃線部分強(qiáng)制轉(zhuǎn)換為枚舉類型即可,如:(PaymentType)paymentTypeInt32。可問題是:在運(yùn)行時(shí)如何動(dòng)態(tài)轉(zhuǎn)換類型呢?比如說我在寫ElegantDAL的時(shí)候,需要將從數(shù)據(jù)庫讀出的一個(gè)類型為int的數(shù)值寫入到要返回的對象的一個(gè)枚舉型字段中,此時(shí)我只有fieldInfo、columnValue和resultObject,然而寫成fieldInfo.SetValue(resultObject, columnValue)就會出現(xiàn)前面提到的錯(cuò)誤,可是我又只有一個(gè)運(yùn)行時(shí)的Type信息(fieldInfo.FieldType),我又不能寫成fieldInfo.SetValue(resultObject, (fieldInfo.FieldType)columnValue)…… 只好將這種情況列為一個(gè)特例處理,而我們的救兵則是Enum.ToObject()方法——你知道有更好的方法解決這個(gè)問題嗎?
?
??????? 到這里,我們簡單的講述了反射的作用以及一些基本的用法,還有很多方面沒有涉及到,有興趣的朋友可以參考MSDN。
?
?
???????
轉(zhuǎn)載于:https://www.cnblogs.com/jaryup/archive/2012/04/28/2474829.html
總結(jié)
- 上一篇: 类 property属性
- 下一篇: 以系统最高权限运行软件