再议C#方法中的反射方式和委托方式

我们将要谈到的是C#方法中的反射方式和委托方式,文中还将给出具体代码,以方便大家测试和实践。

AD:

在开发过程中对静态方法的调用是通过类型名后面加个点而后是调用方法的名称,对类型实例方法的调用是通过new一个对象,而后点加方法名称,这是最熟悉不过的两种方式。还可以通过读取CLR元数据,利用反射进行方法调用。在利用反射方式调用方法时,最重要的两个类是System.Type和System.Reflection.MethodInfo。用MethodInfo类型的Invoke方法调用方法,必须传入目标对象实例的引用。如下:

  1. public class Calculate  
  2. {  
  3. //使用反射可以调用私有方法  
  4. private intAdd(int leftNum, int rightNum)  
  5. {  
  6. return leftNum + rightNum;  
  7. }  
  8.  }  
  9. classProgram  
  10. {  
  11. static voidMain(string[] args)  
  12. {  
  13. //用type.getmethod的方法获取类型方法,BindingFlags设置查找方法的范围  
  14. //本例是公有方法,私有方法而且是非静态的才被查找,如果要查找静态方法  
  15. //需要设置BindingFlags.Static  
  16. MethodInfo method = typeof(Calculate).GetMethod("Add", BindingFlags.Public  
  17. | BindingFlags.NonPublic  
  18. |BindingFlags.Instance);  
  19. if(method == nullreturn 
  20. //调用方法的参数  
  21. object[] paras ={ 10, 20 };  
  22. //目标对象实例:new Calculate()  
  23. objectresult = method.Invoke(new Calculate(), paras);  
  24. Console.WriteLine(result);  
  25. Console.ReadLine();  
  26. }  

委托方式

任何对象都可以调用委托,只要方法返回值以及方法签名和委托声明一样就行。

通过阅读CLR源代码,整理了委托类的重要字段和几个常用方法,自定义的委托类型都派生于MulticastDelegate。

  1. public abstract class Delegate: ICloneable,ISerializable  
  2. {  
  3. // 调用目标对象,实例方法为类型实例引用,静态方法则为null  
  4. internalObject_target;  
  5. //指向调用方法  
  6. internalIntPtr_methodPtr;  
  7. //委托构造器  
  8. protected Delegate(Objecttarget, Stringmethod)  
  9. {  
  10. //省略,具体看以查看clr源代码  
  11. }  
  12. public static Delegate CreateDelegate(Typetype, Objecttarget, Stringmethod)  
  13. {  
  14. //省略,具体看以查看clr源代码  
  15. }  
  16. public static Delegate CreateDelegate(Typetype, Typetarget, Stringmethod)  
  17. {  
  18. //省略,具体看以查看clr源代码  
  19. }  
  20. public static Delegate Combine(paramsDelegate[] delegates) {}  
  21. public static Delegate Combine(Delegatea, Delegateb) {}  
  22. public static Delegate Remove(Delegatesource, Delegatevalue){}  
  23. }  
  24. public abstract class MulticastDelegate: Delegate  
  25. {  
  26. private Object _invocationList;  
  27. protected MulticastDelegate(Objecttarget, Stringmethod) : base(target, method) { }  
  28. protectedMulticastDelegate(Typetarget, Stringmethod): base(target, method) { }  

从源代码可以看出Delegate类提供了几个重载的静态方法CreateDelegate,方法返回值是Delegate类型。如果是实例方法则把对象引用传递给它,如是静态方法则传入对象类型。

  1. publicdelegateintDelegateCaculate(inta,intb);  
  2. publicclassCaculate  
  3. {  
  4. publicintAdd(intnum1, intnum2)  
  5. {  
  6. returnnum1 + num2;  
  7. }  
  8. publicstaticintSubtract(intnum1, intnum2)  
  9. {  
  10.  returnnum2 - num1;  
  11. }  
  12. }  
  13. classProgram  
  14.  {  
  15. staticvoidMain(string[] args)  
  16. {  
  17. Caculatecaculate = newCaculate();  
  18. TypetypeCaculate = typeof(Caculate);  
  19. TypetypeDelegate = typeof(DelegateCaculate);  
  20. DelegateCaculateadd = (DelegateCaculate)Delegate.CreateDelegate(typeDelegate, caculate, "Add");  
  21. DelegateCaculatesubtract = (DelegateCaculate)Delegate.CreateDelegate(typeDelegate, typeCaculate, "Subtract");  
  22. Console.WriteLine("add:"+ add(10, 20));  
  23. Console.WriteLine("subtract:"+ subtract(10, 20));  
  24. Console.ReadLine();  
  25. }  

CreateDelegate需要通过遍历元数据来获取方法句柄。C#语法提供了更便利的方法来调用委托,可以简单通过类型名或者对象名来限定方法,而且不需要通过遍历元数据,C#编译器使用底层CIL的ldftn或许ldvirtftn操作符获取方法地址,相对来说要比CreateDelegate快的多了。上面的Main方法可以改写为

  1. staticvoidMain(string[] args)  
  2. {  
  3. DelegateCaculateadd = newDelegateCaculate(newCaculate().Add);  
  4. DelegateCaculatesubtract = newDelegateCaculate(Caculate.Subtract);  
  5. Console.WriteLine("add:"+ add(10, 20));  
  6. Console.WriteLine("subtract:"+ subtract(10, 20));  
  7. Console.ReadLine();  

可以将多个委托对象放到委托对象数组中,一旦对其调用,CLR将遍历委托数组,对其逐一调用。

  1. publicdelegatevoidDelegateCaculate(inta,intb);  
  2. publicclassCaculate  
  3. {  
  4. publicstaticvoidAdd(intnum1, intnum2)  
  5. {  
  6. Console.WriteLine((num1+ num2));  
  7. }  
  8. publicstaticvoidSubtract(intnum1, intnum2)  
  9. {  
  10. Console.WriteLine((num2- num1));  
  11. }  
  12. }  
  13. classProgram  
  14. {  
  15. staticvoidMain(string[] args)  
  16. {  
  17. DelegateArray(newDelegateCaculate(Caculate.Add), newDelegateCaculate(Caculate.Subtract));  
  18. Console.ReadLine();  
  19. }  
  20. staticvoidDelegateArray(DelegateCaculatea, DelegateCaculateb)  
  21. {  
  22. DelegateCaculatedelChain = null 
  23. delChain = (DelegateCaculate)Delegate.Combine(delChain, a);  
  24. delChain = (DelegateCaculate)Delegate.Combine(delChain, b);  
  25. delChain(10, 20);  
  26. }  

C#提供了更便捷的语法把委托对象添加到委托数组内,可以这样修改上面的DelegateArray方法,

  1. staticvoidDelegateArray(DelegateCaculatea, DelegateCaculateb)  
  2.  {  
  3. DelegateCaculatedelChain = null 
  4. delChain += a;  
  5. delChain+=b;  
  6. delChain(10, 20);  

当执行(DelegateCaculate)Delegate.Combine(delChain, a)时,因为委托数组中只有一个a对象,所以delChain也只是简单的指向a。示意图如下

当执行(DelegateCaculate)Delegate.Combine(delChain, b)是,因为委托数组已经有两个对象了,这时会生成一个新的MulticastDelegate对象让delChain指向它,而_invocationList指向一个委托数组对象,示意图如下

如果还有委托对象加入,将会再次生成一个新的MulticastDelegate对象让delChain指向这个新对象,原来的对象则等待垃圾回收器进行回收,这点可以查看CLR源代码,每添加一个委托对象就调用一次方法NewMulticastDelegate,这个方法返回值是MulticastDelegate。

委托与接口

接口与委托都拥有调用特定方法的能力,所以他们在这点很相像。但是接口需要目标方法的类型声明必须与该接口兼容,而委托可以被任何类型调用,只要该类型的目标方法签名和委托签名匹配即可。

那么何时用委托,何时用接口呢,msdn 总结的非常好,我就直接给粘贴过来了,

委托在以下情况很有用:

1、 调用单个方法。

2、 一个类希望有方法规范的多个实现。

3、 希望允许静态方法实现规范。

4、 希望类似事件的设计模式。

5、 调用方不需要知道或获得实现与委托签名匹配的方法的对象。

6、 实现的提供程序希望只对少数选择组件“分发”规范实现。

7、 需要方法的组合。

接口在以下情况很有用:

1、 规范定义一组相关方法。

2、 类通常只实现规范一次。

3、 接口的调用方希望转换为接口类型或从接口类型转换,以获得其他接口或类。

原文链接:http://www.cnblogs.com/qiuwuyu/archive/2011/08/29/2157230.html

posted @ 2011-10-14 11:16  左正  阅读(1335)  评论(0编辑  收藏  举报