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