zoukankan      html  css  js  c++  java
  • c#委托(delegate)揭秘

    委托是一种定义方法签名的类型。 当实例化委托时,您可以将其实例与任何具有兼容签名的方法相关联。 您可以通过委托实例调用方法。  

    在表面上,委托很简单,使用new来构造委托实例。使用委托实例的变量名来调用回调函数。实际情况是编译器,CLR在幕后做了大量的工作来隐藏其复杂性,只有了解了这些幕后的东西,你才能真正的掌握它、灵活的运用它。

         1、声明委托

    namespace DelegateDemo
    {
        internal delegate void HelloCallBack(string name);
        class Program
        {
            static void Main(string[] args)
            {
    
            }
        }
    }
    

        通过ildasm查看中间代码,如下

    编译器自动生成一个helloCallBack的类,类里面有构造方法,回调方法Invoke,异步回调方法(BeginInvoke,EndInvoke),

    它继承MulticastDelegate类,MulticastDelegate继承Delegate类,c#有两个委托类(Delegate,MulticastDelegate)是有历史原因的,原来是要合并成一个类,但快到发布时间了,合并它需要重新测试,所以Delegate就幸存下来)。

      2、委托的实例化

        internal delegate void HelloCallBack(string name);
        class Program
        {
            static void Main(string[] args)
            {
                HelloCallBack helloShow = new HelloCallBack(ShowName);
    
                Console.ReadLine();
            }
    
            static void ShowName(string name)
            {
                Console.WriteLine(name);
            }        
        }
    

          中间代码如下

         .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint  /                                                                                                 //第一个被执行的方法被称为入口函数
      // Code size       15 (0xf)
      .maxstack  3                                                                                                  //定义函数代码所用堆栈的最大深度
      .locals init ([0] class DelegateDemo.HelloCallBack helloShow)                                //分配一个局部变量helloShow

      IL_0000:  nop                                                                                                 //如果修补操作码,则填充空间,未执行任何有意义的操作

      IL_0001:  ldnull                                                                                                //将空引用推送到计算堆栈上
      IL_0002:  ldftn      void DelegateDemo.Program::ShowName(string)                      //将ShowName的函数指针(非托管指针 native int 类型)推送到计算堆栈上
      IL_0008:  newobj     instance void DelegateDemo.HelloCallBack::.ctor(object, native int) //创建一个新对象,并将对象引用推送到计算堆栈上
      IL_000d:  stloc.0                                                                                               //从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量helloShow中
      IL_000e:  ret                                                                                                    //从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上
    } // end of method Program::Main

      从中间代码我们可以看到,HelloCallBack类构造函数有两个参数,HelloCallBack::.ctor(object,  native int),而我的代码是

    HelloCallBack helloShow = new HelloCallBack(ShowName);只有一个参数,应该编译不过。编译器在这个地方帮我们做了一些东西,

    当它知道要构造的是委托时,就会分析源代码来确定引用的是哪个对象,那个方法。对象引用传递给object,ShowName的函数指针传递给native int

        3、调用回调方法

         源代码如下

        internal delegate void HelloCallBack(string name);
        class Program
        {
            static void Main(string[] args)
            {
                HelloCallBack helloShow = new HelloCallBack(ShowName);
                helloShow("hello");
            }
    
            static void ShowName(string name)
            {
                Console.WriteLine(name);
                Console.ReadLine();
            }         
        }
    

     中间代码如下

    .method private hidebysig static void  Main(string[] args) cil managed
    {
      .entrypoint
      // Code size       27 (0x1b)
      .maxstack  3
      .locals init ([0] class DelegateDemo.HelloCallBack helloShow)
      IL_0000:  nop
      IL_0001:  ldnull
      IL_0002:  ldftn      void DelegateDemo.Program::ShowName(string)
      IL_0008:  newobj     instance void DelegateDemo.HelloCallBack::.ctor(object,
                                                                           native int)
      IL_000d:  stloc.0
      IL_000e:  ldloc.0                                    //将索引 0 处的局部变量加载到计算堆栈上
      IL_000f:  ldstr      "hello"                        //把一个字符串常量装入堆栈
      IL_0014:  callvirt   instance void DelegateDemo.HelloCallBack::Invoke(string) //对对象调用后期绑定方法,并且将返回值推送到计算堆栈上

      IL_0019:  nop
      IL_001a:  ret
    } // end of method Program::Mains

    helloShow("hello") 等价于 helloShow.Invoke("hello");

    完整的代码如下

        internal delegate void HelloCallBack(string name);
        class Program
        {
            static void Main(string[] args)
            {
                HelloCallBack helloShow = new HelloCallBack(ShowName);
                helloShow.Invoke("hello");
     Console.ReadLine();
     }
    
            static void ShowName(string name)
            {
                Console.WriteLine(name);
            }         
        }
    

     Invoke是怎么实现的呢,查看中间代码如下

    .method public hidebysig newslot virtual 
            instance void  Invoke(string name) runtime managed
    {
    } // end of method HelloCallBack::Invoke
    

     runtime managed 表示此方法运行时有CLR处理,我推测类似于

                Delegate[] delegates = helloShow.GetInvocationList();
                for (int i = 0; i < delegates.Length; i++)
                {
                    Delegate callback = delegates[i];
                    MethodInfo method = callback.Method;
                    method.Invoke(helloShow.Target, new object[] { "hello" });
                }

     4、委托链

    委托链是委托对象的集合,利用它,可以调用委托的所有方法

    Delegate有两个公共属性

    Target  获取类实例,当前委托将对其调用实例方法。(静态方法访问空)

    Method 获取委托所表示的方法。

    多播委托的使用如下

    namespace DelegateDemo
    {
        internal delegate void HelloCallBack(string name);
        class Program
        {
            static void Main(string[] args)
            {
                HelloCallBack helloShow = new HelloCallBack(ShowName);
                helloShow += ShowCHName;
                helloShow.Invoke("hello");
                Console.ReadLine();
            }
    
            public static void ShowCHName(string name)
            {
                Console.WriteLine("你好:"+name);
            }    
    
            public static void ShowName(string name)
            {
                Console.WriteLine(name);
            }         
        }
    }
    

     查看MulticastDelegate源码可知,委托链保存在private object _invocationList;

    HelloCallBack helloShow = new HelloCallBack(ShowName);
    

    _invocationList被初始化成object[],数组的第一个元素为new HelloCallBack(ShowName)委托

     helloShow += ShowCHName;
    

     +使用了运算符重载,它实际调用的是Delegate的Combine,-号实际调用的是Delegate的Remove

    
    



  • 相关阅读:
    如何使用idea创建一个java项目
    IntelliJ IDEA 下载安装配置教程
    使用cmd命令输出Hello word
    用js引入css,减少http请求次数,提高响应速度,
    mysql order by limit出现数据丢失问题
    被ASP.NET GridView checkbox选择逼疯的朋友们,请放下你手中的割腕刀
    分发计数器
    mysql 操作
    code site 例子
    kjj
  • 原文地址:https://www.cnblogs.com/50614090/p/2248408.html
Copyright © 2011-2022 走看看