2012-05-24 20 views
6

Recentemente ho imparato a utilizzare i metodi di estensione C# per semplificare gli eventi di chiamata e li ho utilizzati sempre di più. Di recente ho trovato uno strano problema che non capisco, e mi chiedevo se qualcuno potesse spiegarlo.Gestione degli eventi con i metodi di estensione C#

Il problema si verifica quando si tenta di impostare un metodo di estensione dell'handler di eventi come gestore di eventi di un altro evento. Ecco un esempio di quello che sto facendo:

public static class EventHandlerExtensions 
{ 
    public static void Raise<TEventArgs>(
     this EventHandler<TEventArgs> eventHandler, 
     object sender, TEventArgs args) where TEventArgs:EventArgs 
    { 
     if (eventHandler != null) 
     { 
      eventHandler(sender, args); 
     } 
    } 
} 

public class Test 
{ 
    private event EventHandler<EventArgs> EventA; 
    private event EventHandler<EventArgs> EventB; 

    public Test() 
    { 
     Console.WriteLine("::Start"); 
     EventB += EventA.Raise; 
     EventA += (s, a) => Console.WriteLine("Event A raised"); 
     EventB.Raise(this, EventArgs.Empty); 
     Console.WriteLine("::End"); 
    } 
} 

In questo esempio, Eventa dovrebbe essere attivato a seguito di EventB essere attivato. Tuttavia, quando eseguo questo codice, EventB si attiva, ma il metodo di estensione su A non trova alcun listener per esso.

Se cambio l'ordine intorno, tutto funziona bene:

Console.WriteLine("::Start"); 
EventA += (s, a) => Console.WriteLine("Event A raised"); 
EventB += EventA.Raise; 
EventB.Raise(this, EventArgs.Empty); 
Console.WriteLine("::End"); 

Inoltre, chiamando EventA.Raise da una lambda funziona bene:

Console.WriteLine("::Start"); 
EventB += (s, a) => EventA.Raise(s, a); 
EventA += (s, a) => Console.WriteLine("Event A raised"); 
EventB.Raise(this, EventArgs.Empty); 
Console.WriteLine("::End"); 

Questo è solo un semplice esempio, ma io Sto cercando di creare una classe in grado di ri-inviare eventi di fonti di eventi aggiunti nel modo più pulito possibile. Non voglio creare metodi denominati solo per ridispacciare gli stessi eventi, e preferisco non archiviare elenchi di funzioni lambda che posso sganciare dai gestori di eventi in seguito. Per lo più, sono solo curioso di sapere perché questo sta accadendo?

Qualche idea?

+0

Questo potrebbe diventare davvero pessimo se si hanno molti gestori di eventi a cui ci si iscrive. Bella domanda, però !!! :) – IAbstract

+0

Quando scrivo il codice in VS2010, digitando 'EventA.R' subito dopo' Console.WriteLine ("Start"); 'non mostra il metodo di estensione in intelllisense. Lo fa se lo digito poco prima di 'Console.WriteLine (" End ");'. Strano. – Guillaume

risposta

4

È possibile acquisire il valore precedente di EventA nella chiusura mediante la funzione di aumento. Poiché in seguito utilizzi + = cambia il valore di EventA, ma la tua chiusura ha ancora un valore vecchio. Codice

È:

EventB += EventA.Raise; 
EventA += (s, a) => Console.WriteLine("Event A raised"); 

può essere espansa in codice equivalente, che rende chiaro il motivo per cui si diventa vecchi delegato:

var oldEventA = EventA; 
EventB += oldEventA.Raise; // captures old value here 
// now EventA changed to new value 
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");) 

È possibile aggiungere in seguito alla prima EventB += EventA.Raise per verificare che il codice in realtà solleva vecchio evento per A:

EventA += (s, a) => Console.WriteLine("Old Event A raised"); 
2

Oggetti delegati sono immutabili. Molto come gli archi. Pertanto, quando assegni EventA, crei un nuovo oggetto . EventB sta ancora bersagliando il vecchio, quello che non ha ancora assegnato alcun gestore di eventi. Devi scambiare le due dichiarazioni per risolvere il problema.

Problemi correlati