2014-08-29 61 views
10

該文章指出You Can’t Unsubscribe from an Event Using a Lambda Expression爲什麼我不能使用Lambda表達式取消訂閱事件?

E.g.您可以訂閱如下:

d.Barked += (s, e) => Console.WriteLine("Bark: {0}", e); 

,但你不能退訂這樣的:

d.Barked -= (s, e) => Console.WriteLine("Bark: {0}", e); 

爲什麼?這與退訂代理人有什麼區別,例如

EventHandler<string> handler = (s, e) => Console.WriteLine("Bark: {0}", e); 
d.Barked += handler; 

// ... 

d.Barked -= handler; 
+0

http://stackoverflow.com/questions/183367/unsubscribe-anonymous-method-in-c-sharp – 2014-08-29 07:27:38

+0

是的,這與文章中的內容大致相同,但它不能解釋爲什麼。 – DaveDev 2014-08-29 07:28:22

+0

@TimSchmelter不幸的是我的瀏覽器只能看到過去。 – DaveDev 2014-08-29 07:38:55

回答

32

這一切都歸結爲:什麼時候兩個代表爲了委託加/減的目的考慮相同。當取消訂閱時,它基本上使用從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; 

其中MethodOneMethodTwo裏面有他們相同的代碼;它不會取消訂閱任何內容。

這可能是不錯如果編譯器看準了這一點,但它並不需要,因此它是安全的,它不 - 它可能意味着不同的編譯器開始生產非常不同的結果。

作爲一個側面說明;如果確實試圖去重複,可能會非常令人困惑,因爲您還會遇到捕獲上下文的問題 - 那麼在某些情況下它會「工作」而不是其他問題 - 並不明顯 - 可能是最糟糕的情況。

+0

我覺得這是令人傷心的諷刺,我仍然是這裏唯一的上流人。 (在我看來,這幾乎同樣具有諷刺意味 - 這也回答了「如何」更合適;感謝修復我的事件清除心理模型。) – sehe 2014-08-29 08:46:41

+0

優秀的解釋!自從我在C#理解中發現一個缺口已經有一段時間了,但今天我有了一個新的見解。 我需要理解這一點,因爲我想使用匿名方法(Action類型)的引用值來確定它是否已被註冊爲回調。現在,我知道只有在沒有關閉並且可能會導致意想不到的問題時纔會起作用。 – 2015-05-11 15:23:08

相關問題