C#委托

一、委托的概念:c#中的委托类似于C++中的函数指针,提供了程序回调指定方法的机制,能为执行多个函数指定统一的调用方式,减少重复代码,增加编程的灵活性

1.为了方便理解,先简单回顾一下C++的函数指针:

  (1) 假如存在四个基本的函数:

                           ①.输出两整数之和:void Addition(int a, int b){...}

                           ②.输出两数只差:void Minus(int a, int b){...}

                           ③.输出两整数乘积: void  Multiply(int a, int b){...}

                           ④.输出两整数除法结果:void Divide(int a,int b){...}                            

 (2) 现在有个业务想要输出两个整数的和、差,可以定义一个带有函数指针参数的函数实现:

 定义: 

  void HandTest(int (*p)(int,int),int (*q)(int,int),int a,int b )

   {

     int m,n;

     m=(*p)(a,b);

     n=(*q)(a,b);

     printf("%d,\t%d\n",m,n);

  }

 使用:

  void main()

 {  

  HandTest(Addition,Minus,100,10);

}

(3).显然,如果定义一个普通参数的函数直接调用基本函数也可以达到目的,如:void PTest(int a,int b){int m,n;m=Addition(a,b);n=Minus(a,b); printf("%d,\t%d\n",m,n);}, 但是,如果现在有另外的业务要求输出两整数相乘的结果和相除的结果,那么函数指针的优势就体现出来了,原来定义的函数HandTest无需改变,直接传新的实参进行调用:

      HandTest(Multiply,Divide,100,10);

而对于普通参数的函数PTest需要改变函数体,重新定义才能满足新的要求.

可以看出,如果有多个类似业务,带有函数指针参数的函数可以实现同一的调用“接口”,而采用直接调用基本函数的函数则要对应每个业务定义函数(最终会有N个函数),显然函数指针给编程带来了灵活性。

 2.C#中的委托是一种类型,拥有函数指针的优点,又摒弃了函数指针的缺点,内部包含了指向方法的"指针"(即一个引用类型的变量)。

    函数是对实现的封装,委托是对函数的封装,如果一段功能代码封装成为一个函数,我们就能在用户端动态调用,如main函数一般是其它自定义函数的用户.
而对于有相同函数签名的一类函数(相同的返回值,相同的参数个数及对应类型),我们就可以把它们封装到一个委托里面,供主函数动态的以委托为基点动态的选择要执行函数群中的哪个函数.

二、委托的定义:程序员定义的委托均是Delegate的子类

     1.定义方式:public delegate void TestDelegate(int i);

        返回值和括号里的参数类型和数目要和接收委托的方法的一致。接收委托的方法(即该委托对象指向的方法)既可以是静态的也可以是实例的。

       比如接收委托的方法如下:

        public void PrintMsg(int i)

       {

          Console.Write("第{0}个方法!",i)  ;

       }

    2.委托对象的声明:

      TestDelegate d=PrintMsg;

      或

       TestDelegate d=new TestDelegate (PrintMsg);

      //对象d包含了一个指向方法PrintMsg的"指针"通过它可以实现函数回调

   3.执行委托/调用委托方法:

     当执行委托时,.NET检查委托对象(d)并找到其绑定的方法(PrintMsg),然后把参数传递给它并且执行。

      d(0);

      d(1);

     等价于:

     d.Invoke(0);

     d.Invoke(1);

   小贴士:

     关于Invoke,它是个同步调用的方法(即在同一线程下调用),其实委托就是一个类,里面还包含异步调用的方法BenginInvoke、EndInvoke(即在新开辟的线程下调用),有关.NET的异步机制,可参考http://www.cnblogs.com/andyhuang/archive/2008/12/29/1363130.html

 

 三、委托的内部结构

     1.System.Delegate的内部结构:

       

   

     小贴士:绑定一个实例的方法到委托必须同时让委托得到实例方法的代码段和实例对象的信息。

    _target是一个指向目标实例的引用,当委托绑定一个实例方法时,会将该实例赋值给_target,当委托绑定一个静态方法时,_target的值为null;

    _methodPtr是一个指向绑定方法代码段的指针

  2.MulticastDelegate(多播委托)继承于Delegate,其内部还包括_prev,该指针指向委托链中下一个委托对象,程序员自定义委托都直接继承于MulticastDelegate类型,均是多播委托/链式委托

   

   链式委托是由多个委托串成的链表,当链表上的一个委托被回调时,其后继的委托回调回会依次执行。

   (1) 定义一个链式委托:public delegate void TestMultiDelegate();

    方法:①public void printMsg1(){}

          ②public void printMsg2(){}

          ③public void printMsg2(){}

   (2)链式委托声明:TestMultiDelegate handler=new TestMultiDelegate(printMsg1);

                          handler+=new TestMultiDelegate(printMsg2);

                          handler+=new TestMultiDelegate(printMsg3);

                         或

                          TestMultiDelegate handler=printMsg1;

                          TestMultiDelegate handler+=printMsg2;

                          TestMultiDelegate handler+=printMsg3;

                          等价于:

                         TestMultiDelegate handler=new TestMultiDelegate(printMsg1);

                         TestMultiDelegate handler2=new TestMultiDelegate(printMsg2);

                TestMultiDelegate handler3=new TestMultiDelegate(printMsg3);

                         TestMultiDelegate handlerHead=handler+handler2+handler3;

    小贴士:串联委托到委托链表的方式是链表结点的尾部插入方式,新的委托对象会被插入到现有的委托链表的尾部成为新的尾结点。

  (3).链式委托的执行顺序的可控性:

      方法一:在声明阶段就有意识的按目标执行顺序来构建链式委托

      方法二:如果链式委托已经形成,事后执行时非要打乱已形成的顺序来执行,则可用委托的GetInvocationList()方法一次性获取当前委托对象及其后继所有的委托对象,然后按自己意愿操作它们。如:Delegate[] hlist=handler.GetInvocationList();

 小贴士:如果委托的方法是带返回值的,那么链式委托执行时处理每个委托回调方法执行的返回值,需要程序员一一手动接收(用foreach),否则只能接收到最后执行的委托回调方法的返回值

 

 

         

 

posted on 2015-02-25 21:28  JYsharp  阅读(243)  评论(0编辑  收藏  举报