• 委托的内部机制


     

         委托是一种定义方法签名的类型,是对方法的抽象、封装。与委托的签名(由返回类型和参数组成)匹配的任何可访问类和结构中的任何方法都可以分配给该委托,方法可以是静态方法,也可以是实例方法。将一个方法绑定到委托时,C#和CLR允许引用类型的协变性逆变性

    协变性是指方法的返回类型可以派生自委托的返回类型。

    逆变性是指委托的参数类型可以派生自方法的参数类型。

    协变性和逆变性只能用于引用类型,不能用于值类类型或void。所以不能将下面的方法与委托绑定:

    复制代码
            delegate Object DelegateCallBack(FileStream s);
            public int CallBackMethod(Stream s)
            {
                //dosomthing
                return 1;
            }
    复制代码

      而下面的方法是可以进行绑定的:

            public string CallBackMethod(Stream s)
            {
                //dosomthing
                return "1";
            }

    委托具有以下特点:

    1.委托类似于C++函数指针,但它们是类型安全的。

    2.委托允许将方法作为参数进行传递。

    3.委托可以用于定义回调方法。

    4.委托可以链接在一起;例如,可以对一个事件调用多个方法。

    5.方法不必与委托签名完全匹配。

          通过反编译工具可以知道委托类型会被编译成一个类(一个sealed类,所以不允许从委托类型派生任何类型),并且这个类继承自System.MulticastDelegate类(所有委托类型都派生自该类型)。委托继承了MulticastDelegate类的字段、属性和方法,其中三个重要的对于理解委托及委托链的内部机制非常有帮助的是_target, _methodPtr, _invocationList。具体解释一下:

    _target:当委托对象包装一个静态方法时,该值为null。包装实例方法时,该值是回调方法要做操的对象。

    _methodPtr:一个内部的整数值,标示要回调的方法。

    _invocationList:委托链时,引用一个委托数组,否则为null。

    下面通过具体的代码,调试查看上述三个字段的实际赋值情况:

    回调静态方法:

    复制代码
            public delegate void DelegateSample(string content);
            static void Main(string[] args)
            {            DelegateSample staticInstance = SendMessage;
                Console.ReadLine();
            }
            public static void SendMessage(string content)
            {
                Console.WriteLine(content);
            }
    复制代码

    调试可以发现:

    _target并不是为null,而是引用的方法信息,为什么呢?这个我暂时也不知道。但是Target属性的值为null,没有问题,这个属性分装了字段_target,回调静态方法时为null。

    回调实例方法:

    复制代码
            static void Main(string[] args)
            {
                DelegateSample x = new Program().SendQQ;
                Console.ReadLine();
            }
            public void SendQQ(string content)
            {
                Console.WriteLine(content);
            }
    复制代码

    调试查看委托实例x:

    委托链:

            稍微修改一下上面的例子,哎,这个例子改的我自己都快受不了了。

    复制代码
            static void Main(string[] args)
            {
                DelegateSample x = new Program().SendMessage;
                DelegateSample sample = new DelegateSample(new Program().SendQQ);
                sample += SendEmial;
                sample += new Program().SendMessage;
                DelegateSample s = SendEmial;//与 sample 链中SendEmial同一个对象
                sample.Invoke("通知");
                new Program().SendNotice("QQ通知:*****", x);
                Console.ReadLine();
            }
    复制代码

    调试查看委托实例x:可以发现_invocationList被初始化为引用一个委托数组。

    委托实例x:

    委托链数组的第三个元素与委托实例x相同,实际上这两个委托都是方法SendMessage的引用,所以截图中的数值相同也就不大惊小怪了,既然是同一个方法的引用,那么函数的入口地址就应该相同。

    另外一点:在对委托经行+=操作时,当发现当前委托变量已经引用了委托对象时,会新构造一个委托对象,并对新对象进行初始化,最后设置委托变量引用新的委托对象,之前的委托对象以及_invocationList字段引用的委托数组将会被当做垃圾某个时刻回收,-=操作同样会新建委托对象。

     总结:

    1.声明委托的本质是声明一个代表着某一系列方法的类。

    2.委托链就是一个委托,其内部含有一个委托对象数组。

    3.调用委托的本质是调用实例化委托对象中的Invoke方法,委托链则是遍历其委托对象数组,依次调用数组中的方法。

  • 相关阅读:
    吉他
    算法题速查手册
    《信号与系统》系列 Ch04 调制与抽样
    《信号与系统》系列 Ch01 信号与系统
    《信号与系统》系列 Ch05 拉普拉斯变换
    《信号与系统》系列 Ch02 连续信号的时域分析
    《信号与系统》系列 Ch03 连续信号的频域分析
    form提交到controller中文乱码
    js
    阿里巴巴稀疏模型训练引擎DeepRec
  • 原文地址:https://www.cnblogs.com/wfy680/p/12380730.html
Copyright © 2020-2023  润新知