這一切都歸結爲:什麼時候兩個代表爲了委託加/減的目的考慮相同。當取消訂閱時,它基本上使用從Delegate.Remove
邏輯,它參考等效兩位代表如果兩個.Target
和.Method
匹配(至少,對於具有單一的目標方法的委託的簡單情況;多播是更復雜的描述) 。那麼:lambda上的.Method
和.Target
是什麼(假設我們將它編譯爲代表,而不是表達式)?
編譯器實際上有很大的自由在這裏,但什麼發生是:
- 如果拉姆達包括通過參數或變量封閉,編譯器創建一個方法(方法)代表該捕獲上下文(其也可以包括
this
令牌)一個編譯器生成的類; 目標是對此捕獲上下文實例的引用(將由捕獲範圍定義)
- 如果lambda不包含通過參數或變量的閉包,但確實使用每實例狀態經由
this
(隱式或顯式),編譯器創建在當前類型的實例方法(該方法);所述目標是當前實例(this
)
- 否則編譯器創建一個靜態方法(方法),並且目標爲空(順便說一下,在這種情況下它還包括一個漂亮的字段來緩存單一的靜態委託實例 - 所以在這種情況下,只有一個委託曾經每拉姆達創建)
它所不做,不過,與外觀類似的機構,以減少任何比較大量的lambda表達式。所以,我所得到的,當我編譯你的代碼是靜態方法:
[CompilerGenerated]
private static void <Main>b__0(object s, string e)
{
Console.WriteLine("Bark: {0}", e);
}
[CompilerGenerated]
private static void <Main>b__2(object s, string e)
{
Console.WriteLine("Bark: {0}", e);
}
(這裏的Main
只是因爲在我的測試臺的lambda表達式是Main
法裏 - 但最終的編譯器可以選擇任何不可發音名稱它選擇在這裏)
第一種方法是使用由第一拉姆達;第二種方法由第二個lambda使用。所以最終,它不起作用的原因是因爲.Method
不匹配。
在普通的C#而言,它會像做:
obj.SomeEvent += MethodOne;
obj.SomeEvent -= MethodTwo;
其中MethodOne
和MethodTwo
裏面有他們相同的代碼;它不會取消訂閱任何內容。
這可能是不錯如果編譯器看準了這一點,但它並不需要,因此它是安全的,它不選 - 它可能意味着不同的編譯器開始生產非常不同的結果。
作爲一個側面說明;如果確實試圖去重複,可能會非常令人困惑,因爲您還會遇到捕獲上下文的問題 - 那麼在某些情況下它會「工作」而不是其他問題 - 並不明顯 - 可能是最糟糕的情況。
http://stackoverflow.com/questions/183367/unsubscribe-anonymous-method-in-c-sharp – 2014-08-29 07:27:38
是的,這與文章中的內容大致相同,但它不能解釋爲什麼。 – DaveDev 2014-08-29 07:28:22
@TimSchmelter不幸的是我的瀏覽器只能看到過去。 – DaveDev 2014-08-29 07:38:55