再議C#方法中的反射方式和委托方式
在開(kāi)發(fā)過(guò)程中對(duì)靜態(tài)方法的調(diào)用是通過(guò)類(lèi)型名后面加個(gè)點(diǎn)而后是調(diào)用方法的名稱(chēng),對(duì)類(lèi)型實(shí)例方法的調(diào)用是通過(guò)new一個(gè)對(duì)象,而后點(diǎn)加方法名稱(chēng),這是最熟悉不過(guò)的兩種方式。還可以通過(guò)讀取CLR元數(shù)據(jù),利用反射進(jìn)行方法調(diào)用。在利用反射方式調(diào)用方法時(shí),最重要的兩個(gè)類(lèi)是System.Type和System.Reflection.MethodInfo。用MethodInfo類(lèi)型的Invoke方法調(diào)用方法,必須傳入目標(biāo)對(duì)象實(shí)例的引用。如下:
- publicclassCalculate
- {
- //使用反射可以調(diào)用私有方法
- private intAdd(intleftNum, intrightNum)
- {
- returnleftNum + rightNum;
- }
- }
- classProgram
- {
- staticvoidMain(string[] args)
- {
- //用type.getmethod的方法獲取類(lèi)型方法,BindingFlags設(shè)置查找方法的范圍
- //本例是公有方法,私有方法而且是非靜態(tài)的才被查找,如果要查找靜態(tài)方法
- //需要設(shè)置BindingFlags.Static
- MethodInfomethod = typeof(Calculate).GetMethod("Add", BindingFlags.Public
- | BindingFlags.NonPublic
- |BindingFlags.Instance);
- if(method == null) return
- //調(diào)用方法的參數(shù)
- object[] paras ={ 10, 20 };
- //目標(biāo)對(duì)象實(shí)例:new Calculate()
- objectresult = method.Invoke(newCalculate(), paras);
- Console.WriteLine(result);
- Console.ReadLine();
- }
- }
委托方式
任何對(duì)象都可以調(diào)用委托,只要方法返回值以及方法簽名和委托聲明一樣就行。
通過(guò)閱讀CLR源代碼,整理了委托類(lèi)的重要字段和幾個(gè)常用方法,自定義的委托類(lèi)型都派生于MulticastDelegate。
- publicabstractclassDelegate: ICloneable,ISerializable
- {
- // 調(diào)用目標(biāo)對(duì)象,實(shí)例方法為類(lèi)型實(shí)例引用,靜態(tài)方法則為null
- internalObject_target;
- //指向調(diào)用方法
- internalIntPtr_methodPtr;
- //委托構(gòu)造器
- protectedDelegate(Objecttarget, Stringmethod)
- {
- //省略,具體看以查看clr源代碼
- }
- publicstaticDelegateCreateDelegate(Typetype, Objecttarget, Stringmethod)
- {
- //省略,具體看以查看clr源代碼
- }
- publicstaticDelegateCreateDelegate(Typetype, Typetarget, Stringmethod)
- {
- //省略,具體看以查看clr源代碼
- }
- publicstaticDelegateCombine(paramsDelegate[] delegates) {}
- publicstaticDelegateCombine(Delegatea, Delegateb) {}
- publicstaticDelegateRemove(Delegatesource, Delegatevalue){}
- }
- publicabstractclassMulticastDelegate: Delegate
- {
- privateObject_invocationList;
- protectedMulticastDelegate(Objecttarget, Stringmethod) : base(target, method) { }
- protectedMulticastDelegate(Typetarget, Stringmethod): base(target, method) { }
- }
從源代碼可以看出Delegate類(lèi)提供了幾個(gè)重載的靜態(tài)方法CreateDelegate,方法返回值是Delegate類(lèi)型。如果是實(shí)例方法則把對(duì)象引用傳遞給它,如是靜態(tài)方法則傳入對(duì)象類(lèi)型。
- publicdelegateintDelegateCaculate(inta,intb);
- publicclassCaculate
- {
- publicintAdd(intnum1, intnum2)
- {
- returnnum1 + num2;
- }
- publicstaticintSubtract(intnum1, intnum2)
- {
- returnnum2 - num1;
- }
- }
- classProgram
- {
- staticvoidMain(string[] args)
- {
- Caculatecaculate = newCaculate();
- TypetypeCaculate = typeof(Caculate);
- TypetypeDelegate = typeof(DelegateCaculate);
- DelegateCaculateadd = (DelegateCaculate)Delegate.CreateDelegate(typeDelegate, caculate, "Add");
- DelegateCaculatesubtract = (DelegateCaculate)Delegate.CreateDelegate(typeDelegate, typeCaculate, "Subtract");
- Console.WriteLine("add:"+ add(10, 20));
- Console.WriteLine("subtract:"+ subtract(10, 20));
- Console.ReadLine();
- }
- }
CreateDelegate需要通過(guò)遍歷元數(shù)據(jù)來(lái)獲取方法句柄。C#語(yǔ)法提供了更便利的方法來(lái)調(diào)用委托,可以簡(jiǎn)單通過(guò)類(lèi)型名或者對(duì)象名來(lái)限定方法,而且不需要通過(guò)遍歷元數(shù)據(jù),C#編譯器使用底層CIL的ldftn或許ldvirtftn操作符獲取方法地址,相對(duì)來(lái)說(shuō)要比CreateDelegate快的多了。上面的Main方法可以改寫(xiě)為
- staticvoidMain(string[] args)
- {
- DelegateCaculateadd = newDelegateCaculate(newCaculate().Add);
- DelegateCaculatesubtract = newDelegateCaculate(Caculate.Subtract);
- Console.WriteLine("add:"+ add(10, 20));
- Console.WriteLine("subtract:"+ subtract(10, 20));
- Console.ReadLine();
- }
可以將多個(gè)委托對(duì)象放到委托對(duì)象數(shù)組中,一旦對(duì)其調(diào)用,CLR將遍歷委托數(shù)組,對(duì)其逐一調(diào)用。
- publicdelegatevoidDelegateCaculate(inta,intb);
- publicclassCaculate
- {
- publicstaticvoidAdd(intnum1, intnum2)
- {
- Console.WriteLine((num1+ num2));
- }
- publicstaticvoidSubtract(intnum1, intnum2)
- {
- Console.WriteLine((num2- num1));
- }
- }
- classProgram
- {
- staticvoidMain(string[] args)
- {
- DelegateArray(newDelegateCaculate(Caculate.Add), newDelegateCaculate(Caculate.Subtract));
- Console.ReadLine();
- }
- staticvoidDelegateArray(DelegateCaculatea, DelegateCaculateb)
- {
- DelegateCaculatedelChain = null
- delChain = (DelegateCaculate)Delegate.Combine(delChain, a);
- delChain = (DelegateCaculate)Delegate.Combine(delChain, b);
- delChain(10, 20);
- }
- }
C#提供了更便捷的語(yǔ)法把委托對(duì)象添加到委托數(shù)組內(nèi),可以這樣修改上面的DelegateArray方法,
- staticvoidDelegateArray(DelegateCaculatea, DelegateCaculateb)
- {
- DelegateCaculatedelChain = null
- delChain += a;
- delChain+=b;
- delChain(10, 20);
- }
當(dāng)執(zhí)行(DelegateCaculate)Delegate.Combine(delChain, a)時(shí),因?yàn)槲袛?shù)組中只有一個(gè)a對(duì)象,所以delChain也只是簡(jiǎn)單的指向a。示意圖如下
當(dāng)執(zhí)行(DelegateCaculate)Delegate.Combine(delChain, b)是,因?yàn)槲袛?shù)組已經(jīng)有兩個(gè)對(duì)象了,這時(shí)會(huì)生成一個(gè)新的MulticastDelegate對(duì)象讓delChain指向它,而_invocationList指向一個(gè)委托數(shù)組對(duì)象,示意圖如下
如果還有委托對(duì)象加入,將會(huì)再次生成一個(gè)新的MulticastDelegate對(duì)象讓delChain指向這個(gè)新對(duì)象,原來(lái)的對(duì)象則等待垃圾回收器進(jìn)行回收,這點(diǎn)可以查看CLR源代碼,每添加一個(gè)委托對(duì)象就調(diào)用一次方法NewMulticastDelegate,這個(gè)方法返回值是MulticastDelegate。
委托與接口
接口與委托都擁有調(diào)用特定方法的能力,所以他們?cè)谶@點(diǎn)很相像。但是接口需要目標(biāo)方法的類(lèi)型聲明必須與該接口兼容,而委托可以被任何類(lèi)型調(diào)用,只要該類(lèi)型的目標(biāo)方法簽名和委托簽名匹配即可。
那么何時(shí)用委托,何時(shí)用接口呢,msdn 總結(jié)的非常好,我就直接給粘貼過(guò)來(lái)了,
委托在以下情況很有用:
1、 調(diào)用單個(gè)方法。
2、 一個(gè)類(lèi)希望有方法規(guī)范的多個(gè)實(shí)現(xiàn)。
3、 希望允許靜態(tài)方法實(shí)現(xiàn)規(guī)范。
4、 希望類(lèi)似事件的設(shè)計(jì)模式。
5、 調(diào)用方不需要知道或獲得實(shí)現(xiàn)與委托簽名匹配的方法的對(duì)象。
6、 實(shí)現(xiàn)的提供程序希望只對(duì)少數(shù)選擇組件“分發(fā)”規(guī)范實(shí)現(xiàn)。
7、 需要方法的組合。
接口在以下情況很有用:
1、 規(guī)范定義一組相關(guān)方法。
2、 類(lèi)通常只實(shí)現(xiàn)規(guī)范一次。
3、 接口的調(diào)用方希望轉(zhuǎn)換為接口類(lèi)型或從接口類(lèi)型轉(zhuǎn)換,以獲得其他接口或類(lèi)。
原文鏈接:http://www.cnblogs.com/qiuwuyu/archive/2011/08/29/2157230.html