2011-12-06 31 views
35

Eventuali duplicati:
How to pass an event to a method?Passare un evento come un parametro a un metodo

E 'possibile passare un evento come un parametro a un metodo?

Ad esempio, il seguente metodo sottoscrive l'evento, funziona, e si cancella da dell'evento:

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 
    } 
} 

Codice cliente:

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

Se non è possibile, come faccio a raggiungere la logica simile in altri modi? Devo passare una coppia di delegati per i metodi di iscrizione/annullamento dell'iscrizione?

+0

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

risposta

40

Si è infatti scoperto che gli eventi non sono "di prima classe" in C#; non è possibile passare in giro un evento come dati. È possibile passare circa un delegato a un metodo associato a un destinatario come oggetto di prima classe effettuando un delegato. Puoi passare a un riferimento a qualsiasi variabile come oggetto (principalmente) di prima classe. (Dico "per lo più" perché i riferimenti alle variabili non possono essere memorizzati nei campi, memorizzati negli array e così via, sono molto limitati rispetto ad altri tipi di dati.) Puoi passare un tipo ottenendo il suo oggetto Type e passando in giro

Ma non c'è modo di passare direttamente come dati un evento, una proprietà, un indicizzatore, un costruttore o un distruttore associati a una particolare istanza. Il meglio che puoi fare è fare un delegato (o un paio di delegati) da un lambda, come suggerisci tu. Oppure, ottenere l'oggetto riflesso associato all'evento e passarlo in giro, insieme all'istanza.

+0

C'è un'eccezione a questa regola: è possibile passare un evento come parametro a un metodo quando l'evento che si sta passando viene dichiarato nella stessa classe che sta effettuando la chiamata. Come nella classe A {evento pubblico SomeDelegate YourEvent; CallMe (SomeDelegate theEvent) {theEvent (this, args); }} –

+0

@ILIABROUDNO: Non vedo dove nel tuo piccolo campione "YourEvent" è mai passato a nulla. –

+0

Il mio male. Metti vuoto di fronte a CallMe e poi aggiungi un altro metodo alla classe A: void OtherMethod() {CallMe (YourEvent);} Ora stai passando YourEvent come parametro. –

25

No, sfortunatamente no.

Se si guarda Reactive Extensions, questo ha un problema simile. Tre opzioni che usano (IIRC - è stato un po 'da quando ho guardato):

  • passaggio nell'area corrispondente EventInfo e chiamarlo con la riflessione
  • passare il nome della manifestazione (e l'obiettivo, se necessario,) e chiamarlo con la riflessione
  • pass delegati di sottoscrizione e di disiscrizione

la chiamata in quest'ultimo caso sarebbe qualcosa di simile:

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

e la dichiarazione sarebbe:

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

Hai mescolato 'Click' e' Client' nell'esempio. – CodesInChaos

3

Stai cercando di aggirare la sicurezza di tipo, e non è possibile farlo senza l'utilizzo di riflessione. Ti mostrerò un esempio ancora più semplice di ciò che stai cercando di fare.

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

C# non funziona in questo modo.Come fa il compilatore a sapere che tutti gli T s hanno il metodo method? Non lo fa e non può. Allo stesso modo, non tutti gli TElement nel tuo codice avranno un evento Click ad esempio.

Sembra che si voglia impostare un gestore di eventi monouso su un insieme di oggetti. È possibile farlo abbastanza facilmente ...

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

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

Questo, ovviamente, funziona solo con i pulsanti, ma mentre scrivo questo, vedo Jon Skeet si è dimostrato una soluzione più generale, quindi mi finisce qui.

+1

Sì, hai ragione riguardo al tipo di sicurezza e riflessione. – altso

Problemi correlati