??先來看一段代碼:
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可執(zhí)行文件胳蛮,并反編譯IL得到代碼如下:
??接著再看AClass是什么樣子的:
??根據(jù)圖上可知,委托MyDelegate最終會被生成為一個類呜达,并且繼承于MulticastDelegate乙漓,MulticastDelegate繼承Delegate吨铸,而myDelegate則是它的實例债蜜。當我們對委托+= 操作時候钙勃,其實是調(diào)用了Delegate.Combine()函數(shù)蛛碌,如上圖的匿名函數(shù),會被傳進Combine中辖源,并返回一個新的Delegate對象給myDelegate蔚携。-=操作會調(diào)用Remove函數(shù)。不知道你注意到?jīng)]有克饶,當我們寫一個匿名函數(shù)傳遞給委托時候酝蜒,會被生成一個函數(shù),例如圖一的b__0_1和b__0_2矾湃,也就是說每次綁定的匿名函數(shù)都會被生成一個帶名字的函數(shù)亡脑,哪怕你寫的匿名函數(shù)一模一樣,編譯后都不是同一個東西邀跃。但如果不是匿名函數(shù)远豺,像LocalFunction,委托的+=和-=都是對同一個函數(shù)操作坞嘀。
??我們不妨再看看源碼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是虛函數(shù)躯护,會調(diào)用帶子類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);
}
看到這里大概都能猜到了棺滞,每個委托類無非就是包含了一個數(shù)組,數(shù)組記錄了多個Delegate矢渊,當我們編寫+=继准,把回調(diào)函數(shù)的包裝類MulticastDelegate的數(shù)據(jù)合并到另一個MulticastDelegate中,一旦執(zhí)行invoke矮男,便調(diào)用所有的回調(diào)函數(shù)移必。
Author : SunnyDecember
Date : 2019.11.16
原文