通過檢查委託人的工作方式以及編譯時創建的MSIL,這一點相對明顯。首先讓我們注意到System.Action
是一個System.Delegate
,它有ICloneable
接口。
當你編譯這個程序,在這裏幾乎是你會得到什麼:
IL_0015: newobj instance void [mscorlib]System.Action::.ctor(object,
native int)
IL_001a: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_001f: castclass [mscorlib]System.Action
IL_0024: stfld class [mscorlib]System.Action ConsoleTests.Program6/Chunk::OnTrigger
IL_0029: ldloc.0
IL_002a: ldfld class ConsoleTests.Program6/Leaf ConsoleTests.Program6/Chunk::leaf
IL_002f: dup
IL_0030: ldfld class [mscorlib]System.Action ConsoleTests.Program6/Leaf::OnTrigger
IL_0035: ldloc.0
IL_0036: ldfld class [mscorlib]System.Action ConsoleTests.Program6/Chunk::OnTrigger
IL_003b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0040: castclass [mscorlib]System.Action
IL_0045: stfld class [mscorlib]System.Action ConsoleTests.Program6/Leaf::OnTrigger
IL_004a: ldloc.0
IL_004b: dup
IL_004c: ldfld class [mscorlib]System.Action ConsoleTests.Program6/Chunk::OnTrigger
IL_0051: ldnull
IL_0052: ldftn void ConsoleTests.Program6::World()
IL_0058: newobj instance void [mscorlib]System.Action::.ctor(object,
native int)
IL_005d: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0062: castclass [mscorlib]System.Action
IL_0067: stfld class [mscorlib]System.Action ConsoleTests.Program6/Chunk::OnTrigger
IL_006c: ldloc.0
IL_006d: ldfld class ConsoleTests.Program6/Leaf ConsoleTests.Program6/Chunk::leaf
IL_0072: callvirt instance void ConsoleTests.Program6/Leaf::Go()
注意,當你在一個委託做一個+=
,編譯器轉換是爲Combine
呼叫,它結合了當前委託與通過在一個。現在看看IL_004b,這是一個dup
調用。
從MSDN:「的評價堆棧上覆制當前最上面的值,然後按下複製到計算堆棧」。
這通知運行時複製堆棧上的對象,因爲這是ICloneable
,它創建的對象的副本。現在,當您向其中添加其他代表時,您並未將它添加到兩個代理中,只是您正在操作的代理中的一個,因此保留原來的Leaf
觸發器。
的幾乎等效代碼是這樣的:
public Tree()
{
var chunk2 = new Chunk();
chunk2.OnTrigger += Hello; // OK: will be called
chunk2.leaf.OnTrigger = (Action)chunk2.OnTrigger.Clone();
chunk2.OnTrigger += World; // NOT: not be called
chunk2.leaf.Go();
}
這可能使更清晰一點關於什麼是怎麼回事。
正如一個側面說明你給你的事件的名稱是非標準的。引發一個事件'OnTrigger'的事件是正常的,但事件本身通常被簡稱爲'Trigger'。所以,如果你的'Leaf'類的方法'Go'應該是'OnTrigger',並且事件應該是'Trigger',以使它們更像標準。 – Enigmativity