先来看一段代码:
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得到代码如下:

接着再看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
原文
网友评论