2011-12-06 53 views
35

可能重複:
How to pass an event to a method?傳遞事件作爲參數的方法

是否有可能通過一個事件作爲參數的方法?

例如,下面的方法訂閱事件,不工作,並從事件退訂:

void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
     IEnumerable<TElement> elements, 
     ??? elementEvent) 
    where TEventArgs: EventArgs 
{ 
    EventHandler<TEventArgs> handler = (sender, e) => { /* Handle an event */ }; 

    foreach (var element in elements) 
    { 
     // Subscribe somehow 
     element.elementEvent += handler 
    } 

    // Do things 

    foreach (var element in elements) 
    { 
     // Unsubscribe somehow 
     element.elementEvent -= handler 
    } 
} 

客戶端代碼:

var elements = new [] { new Button(), new Button() }; 
SubscribeDoAndUnsubscribe(elements, ??? /* e => e.Click */); 

如果這是不可能的,我該如何實現其他方面的類似邏輯?我可以通過一對代表進行訂閱/取消訂閱嗎?

+0

看這裏:http://stackoverflow.com/questions/8022406/passing-an-event-and-a-delegate-event-handler-into-a-generic-helper-method/8022448#8022448 –

回答

40

事實上,你已經發現事件不是C#中的「頭等」您無法將事件作爲數據傳遞。您可以通過代理將代表傳遞給與接收方相關聯的方法,作爲第一級對象。您可以將作爲(主要)第一類對象的參考傳遞給任何變量。 (我說「主要」,因爲變量的引用不能存儲在字段中,存儲在數組中等等;與其他類型的數據相比,它們受到高度限制)。可以通過獲取其Type對象來傳遞類型,通過那個。

但是沒有辦法直接傳遞數據作爲與特定實例相關聯的事件,屬性,索引器,構造函數或析構函數。你可以做的最好的做法是讓一個代表(或一對代表)出於lambda,如你所建議的。或者,獲取與事件關聯的反射對象,並將其與實例一起傳遞。

+0

有該規則的一個例外:當傳遞的事件在進行此調用的同一類中聲明時,您可以將事件作爲參數傳遞給方法。就像在A類{公共事件SomeDelegate YourEvent; CallMe(SomeDelegate theEvent){theEvent(this,args); }} –

+0

@ILIABROUDNO:我沒有看到你的小樣本里有'YourEvent'永遠傳遞給任何東西。 –

+0

我的不好。把void放在CallMe前面,然後向類A中添加另一個方法:void OtherMethod(){CallMe(YourEvent);}現在你傳遞YourEvent作爲參數。 –

25

不,不幸的不是。

如果你看看Reactive Extensions,那會有類似的問題。有三個選項,他們使用(IIRC - 它已經有一段時間,因爲我已經看了):

  • 通在相應EventInfo與反射調用它的事件的名稱
  • 通(和目標,如果必要),並與反射調用它代表訂閱
  • 通行證和退訂

呼叫在後一種情況下會是這樣的:

SubscribeAndDoUnsubscribe(elements, 
          handler => e.Click += handler, 
          handler => e.Click -= handler); 

,並聲明將是:

void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
     IEnumerable<TElement> elements, 
     Action<EventHandler<TEventArgs>> subscription, 
     Action<EventHandler<TEventArgs>> unsubscription) 
    where TEventArgs: EventArgs 
+0

您在示例中混合了'Click'和'Client'。 – CodesInChaos

3

你試圖繞過類型安全,你不能在不使用反射這樣做。我會告訴你一個更簡單的例子,你想要做什麼。

void DoSomethingOnSomethingElse(T obj, Action method) 
{ 
    obj.method(); 
} 

C#不能這樣工作。編譯器如何知道所有的T都有方法method?它不,也不能。同樣,例如,代碼中的每個TElement都不會有事件Click

這聽起來像你只是想在一組對象上設置一個使用事件處理程序。你可以這樣做很容易...

EventHandler handler = null; 
handler = (s,e) => 
{ 
    DoSomething(e); 
    var b = (Button) s; 
    b.Click -= handler; 
} 

foreach (var button in buttons) 
{ 
    button.Click += handler; 
} 

顯然,這僅與按鈕的工作原理,但我寫這篇文章,我看到喬恩斯基特向您展示了一個更通用的解決方案,所以我會在這裏結束。

+1

是的,你是對的類型安全和反思。 – altso