2008-10-08 30 views
192

是否可以從事件取消訂閱匿名方法?在C中取消訂閱匿名方法#

如果我同意這樣一個事件:

void MyMethod() 
{ 
    Console.WriteLine("I did it!"); 
} 

MyEvent += MyMethod; 

我可以取消訂閱這樣的:

MyEvent -= MyMethod; 

但如果我訂閱使用匿名方法:

MyEvent += delegate(){Console.WriteLine("I did it!");}; 

是否可以取消訂閱這個匿名方法?如果是這樣,怎麼樣?

+1

至於*爲什麼*你不能這樣做:http://stackoverflow.com/a/25564492/23354 – 2014-08-29 08:50:52

回答

195
Action myDelegate = delegate(){Console.WriteLine("I did it!");}; 

MyEvent += myDelegate; 


// .... later 

MyEvent -= myDelegate; 

只是保留對委託人的引用。

17

從內存中,當涉及到使用匿名方法創建的委託的等價性時,規範明確無法保證行爲。

如果您需要取消訂閱,您應該使用「普通」方法或將代理保留在其他地方,以便您可以退訂與您用於訂閱的代理完全相同的代理。

+0

我喬恩,你是什麼意思?我不明白。 「J c」公開的解決方案是否無法正常工作? – 2012-10-17 13:33:00

+0

@EricOuellet:該答案基本上是「將代理保留在其他地方,以便您可以退訂與您用於訂閱的代理完全相同的代碼」。 – 2012-10-17 13:38:38

+0

喬恩,我很抱歉,我多次閱讀你的答案,試圖弄清楚你的意思,以及「Jc」解決方案不使用同一個代理來訂閱和取消訂閱的地方,但我無法做到。不知你可以給我一個解釋你所說的話的文章嗎?我知道你的名聲,我真的很想明白你的意思,任何你可以鏈接到的東西都會很感激。 – 2012-10-18 12:59:23

6

類瘸做法:

public class SomeClass 
{ 
    private readonly IList<Action> _eventList = new List<Action>(); 

    ... 

    public event Action OnDoSomething 
    { 
    add { 
     _eventList.Add(value); 
    } 
    remove { 
     _eventList.Remove(value); 
    } 
    } 
} 
  1. 覆蓋的情況下添加/刪除方法。
  2. 保留這些事件處理程序的列表。
  3. 需要時清除全部並重新添加其他。

這可能不起作用,或者是最有效的方法,但應完成工作。

133

一種技術是聲明一個變量來保存匿名方法,然後匿名方法本身就可以使用該方法。這對我來說很有效,因爲理想的行爲是在事件處理後退訂。

例子:

MyEventHandler foo = null; 
foo = delegate(object s, MyEventArgs ev) 
    { 
     Console.WriteLine("I did it!"); 
     MyEvent -= foo; 
    }; 
MyEvent += foo; 
16

在3.0可以簡化爲:如果你希望能夠控制退訂

MyHandler myDelegate =()=>Console.WriteLine("I did it!"); 
MyEvent += myDelegate; 
... 
MyEvent -= myDelegate; 
2

那麼你需要去你接受的答案指示的路線。但是,如果您只關心在訂閱類超出範圍時清除引用,那麼還有另一個(稍微複雜的)解決方案,它涉及使用弱引用。我剛剛在此主題上發佈了question and answer

9

爲了將事件的調用列表返回給調用者,您不需要保留對任何代理的引用,而可以調用您的類。基本上,你可以寫這樣的事情(假設MyEvent是內部MyClass的聲明):

public class MyClass 
{ 
    public event EventHandler MyEvent; 

    public IEnumerable<EventHandler> GetMyEventHandlers() 
    { 
     return from d in MyEvent.GetInvocationList() 
      select (EventHandler)d; 
    } 
} 

這樣你就可以訪問外部MyClass的整個調用列表,並取消任何你想要的處理程序。例如:

myClass.MyEvent -= myClass.GetMyEventHandlers().Last(); 

我已經寫了關於此tecnique here的完整信息。

0

,如果你想指與此委託一些對象,可能是你可以使用Delegate.CreateDelegate(類型,對象目標的MethodInfo MethodInfo的) .NET考慮委託的目標和MethodInfo的

1

一個簡單的解決方案等於:

只是將eventhandle變量作爲參數傳遞給它自己。 事件,如果你有,你不能訪問,因爲多線程的原始創建的變量的情況下,你可以使用這個:

MyEventHandler foo = null; 
foo = (s, ev, mehi) => MyMethod(s, ev, foo); 
MyEvent += foo; 

void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance) 
{ 
    MyEvent -= myEventHandlerInstance; 
    Console.WriteLine("I did it!"); 
} 
0

如果最好的辦法就是保持對訂閱的事件處理程序的參考,這可以使用來實現一本字典。

在這個例子中,我必須使用匿名方法來包含一組DataGridView的mergeColumn參數。

在啓用參數設置爲true的情況下使用MergeColumn方法時,使用false時禁用該事件會啓用該事件。

static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>(); 

public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) { 

    if(enable) { 
     subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns); 
     dg.Paint += subscriptions[dg]; 
    } 
    else { 
     if(subscriptions.ContainsKey(dg)) { 
      dg.Paint -= subscriptions[dg]; 
      subscriptions.Remove(dg); 
     } 
    } 
} 
2

由於C# 7.0 local functions功能已被釋放,該方法通過suggested成爲J c很整潔。

void foo(object s, MyEventArgs ev) 
{ 
    Console.WriteLine("I did it!"); 
    MyEvent -= foo; 
}; 
MyEvent += foo; 

因此,說實話,您在這裏沒有一個匿名函數作爲變量。但是我認爲在你的案例中使用它的動機可以應用於本地功能。