C#的delegate原理

  先来看一段代码:

class AClass
{
    public delegate int MyDelegate(string str);
    public MyDelegate myDelegate;
}

class Program
{
    static void Main(string[] args)
    {
        int LocalFunction(string a)
        {
            return 789;
        }

        AClass aClass = new AClass();
        aClass.myDelegate += t => { return 123; };
        aClass.myDelegate -= t => { return 456; };
        aClass.myDelegate -= LocalFunction;
        aClass.myDelegate += LocalFunction;

        aClass.myDelegate = new AClass.MyDelegate(LocalFunction);
        aClass.myDelegate?.Invoke("");
    }
}

  编译生成exe可执行文件,并反编译IL得到代码如下:


反编译IL得到的结果(图一)

  接着再看AClass是什么样子的:


AClass(图二)

  根据图上可知,委托MyDelegate最终会被生成为一个类,并且继承于MulticastDelegate,MulticastDelegate继承Delegate,而myDelegate则是它的实例。当我们对委托+= 操作时候,其实是调用了Delegate.Combine()函数,如上图的匿名函数,会被传进Combine中,并返回一个新的Delegate对象给myDelegate。-=操作会调用Remove函数。不知道你注意到没有,当我们写一个匿名函数传递给委托时候,会被生成一个函数,例如图一的b__0_1和b__0_2,也就是说每次绑定的匿名函数都会被生成一个带名字的函数,哪怕你写的匿名函数一模一样,编译后都不是同一个东西。但如果不是匿名函数,像LocalFunction,委托的+=和-=都是对同一个函数操作。

  我们不妨再看看源码Delegate.Combine()怎么玩转的:

public static Delegate Combine(Delegate a, Delegate b)
{
    if (a == null)
    {
        return b;
    }
    return a.CombineImpl(b);
}

protected virtual Delegate CombineImpl(Delegate d)
{
    throw new MulticastNotSupportedException(Environment.GetResourceString("Multicast_Combine"));
}

  CombineImpl是虚函数,会调用带子类MulticastDelegate.CombineImpl(),代码太多,只贴一部分:

protected sealed override Delegate CombineImpl(Delegate follow)
{
    if (follow == null)
    {
        return this;
    }
    if (!Delegate.InternalEqualTypes(this, follow))
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));
    }
    MulticastDelegate multicastDelegate = (MulticastDelegate)follow;
    int num = 1;
    object[] array = multicastDelegate._invocationList as object[];
    if (array != null)
    {
        num = (int)multicastDelegate._invocationCount;
    }
    object[] array2 = this._invocationList as object[];
    int num2;
    object[] array3;
    if (array2 == null)
    {
        num2 = 1 + num;
        array3 = new object[num2];
        array3[0] = this;
        if (array == null)
        {
            array3[1] = multicastDelegate;
        }
        else
        {
            for (int i = 0; i < num; i++)
            {
                array3[1 + i] = array[i];
            }
        }
        return this.NewMulticastDelegate(array3, num2);
    }

看到这里大概都能猜到了,每个委托类无非就是包含了一个数组,数组记录了多个Delegate,当我们编写+=,把回调函数的包装类MulticastDelegate的数据合并到另一个MulticastDelegate中,一旦执行invoke,便调用所有的回调函数。

Author : SunnyDecember
Date : 2019.11.16
原文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容