• 委托的内部机制


         委托是一种定义方法签名的类型,是对方法的抽象、封装。与委托的签名(由返回类型和参数组成)匹配的任何可访问类和结构中的任何方法都可以分配给该委托,方法可以是静态方法,也可以是实例方法。将一个方法绑定到委托时,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方法,委托链则是遍历其委托对象数组,依次调用数组中的方法。

  • 相关阅读:
    LeetCode--371--两整数之和
    《Cracking the Coding Interview》——第17章:普通题——题目10
    《Cracking the Coding Interview》——第17章:普通题——题目9
    《Cracking the Coding Interview》——第17章:普通题——题目8
    《Cracking the Coding Interview》——第17章:普通题——题目7
    《Cracking the Coding Interview》——第17章:普通题——题目6
    《Cracking the Coding Interview》——第17章:普通题——题目5
    《Cracking the Coding Interview》——第17章:普通题——题目4
    《Cracking the Coding Interview》——第17章:普通题——题目3
    《Cracking the Coding Interview》——第17章:普通题——题目2
  • 原文地址:https://www.cnblogs.com/ashleyboy/p/4827752.html
Copyright © 2020-2023  润新知