CLR VIA

委托

  • 所有委托类型都派生自MulticastDelegate(间接继承Delegate),其中三个重要的非公共分为别_target、_methodPtr、_invocationList,其在CLR中都有具体解释
    image-20220519220646401

  • 所有委托都有一个构造器,它获取两个参数:一个是对象引用,另一个是引用了回调方法的整数,在Invoke被调用时,它使用私有字段_target和_methodPtr在指定对象上调用包装好的回调方法

  • 所有的委托对象都是一个包装器,其中包装了一个方法和调用该方法时需要操作的对象,包装器之间通过_invocationList之间进行连接,作为委托链来使用。

  • 对于+=和-=进行了封装,通过IL代码可以看到+=其实是调用了Delegate.Combine,而-=其实是调用了Delegate.Remove

    • Combine:以Combine(fbChain,fb1)为例子

      • 若发现fbChain已引用了一个委托对象,会构造一个新的委托对象,对其_target和_methodPtr进行初始化,同时将_invocationList初始化为一个数组,数组中对应元素会指向对应的委托fb1(类似深度拷贝)
      • 若发现是null,则直接赋予fb1中的值,fbChain变为fb1的引用
      • 对于多播委托的添加(若fb1为多播委托),则会将fb1的_invocationList中的所有委托顺序加入到fbChain中,如果超过_invocationList中的length则会进行扩容操作
        放上对应的部分源码:
        image-20220519230637406

      下方是CLR中对应截图

      image-20220519222813890

      image-20220519232702682

    • Remove:以Remove(fbChain,fb1)为例子
      其扫描第一个实参(例子中为fbChain)所引用的委托数组(从末尾向索引0扫描),Remove查找的是_target和_methodPtr字段与第二个实参(fb1)中字段相同的委托

      • 如果找到匹配的委托,并且数组中只剩余一个数据项,就返回该数据项
      • 如果找到匹配的委托,并且数组中还剩余多个数据项,就新建一个委托对象,初始化_invocationList数组并将除删除数据外的委托放入其中,并返回引用。如果从链中删除了仅有的一个元素,Remove会返回null
      • 对于多播委托的移除,会在当前索引处顺序判断是否存在某个部分与该多播委托相同,相同则进行移除
        放上对应的源码:
        image-20220519230709454
        image-20220519230730581

      PS:每次Remove只能从链中删除一个委托,它不会删除匹配target和_methodPtr字段的所有委托