2009-04-03 14 views
9

Di seguito è riportato il programma che ho utilizzato per il test. Esso stampa (come previsto):Come funzionano gli eventi virtuali in C#?

Raise A 
Event from A 
Raise B 
Event from B 

Ora, se cambiamo prime due righe del principale di essere:

 A a = new B(); 
     B b = new B(); 

il programma stamperà:

Raise A 
Raise B 
Event from B 

quali è inoltre, poiché l'evento di sovrascrittura nasconde il campo di backing privato nella classe base e quindi gli eventi attivati ​​dalla classe base non sono visibili ai client della classe derivata.

Ora sto cambiando le stesse linee di:

B b = new B(); 
A a = b; 

e il programma si avvia la stampa:

Raise A 
Raise B 
Event from A 
Event from B 

Cosa sta succedendo?

class A 
{ 
    public virtual event EventHandler VirtualEvent; 
    public void RaiseA() 
    { 
     Console.WriteLine("Raise A"); 
     if (VirtualEvent != null) 
     { 
      VirtualEvent(this, EventArgs.Empty); 
     } 
    } 
} 
class B : A 
{ 
    public override event EventHandler VirtualEvent; 
    public void RaiseB() 
    { 
     Console.WriteLine("Raise B");    
     if (VirtualEvent != null) 
     { 
      VirtualEvent(this, EventArgs.Empty); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     A a = new A(); 
     B b = new B(); 

     a.VirtualEvent += (s, e) => Console.WriteLine("Event from A"); 
     b.VirtualEvent += (s, e) => Console.WriteLine("Event from B"); 

     a.RaiseA(); 
     b.RaiseB(); 
    } 
} 
+0

Articolo "Eventi virtuali in C#: qualcosa è andato storto" - http://www.viva64.com/en/b/0453/ –

risposta

12

Abbiamo una singola istanza (di B) che ha i seguenti campi:

  • A.VirtualEvent: nulli
  • B.VirtualEvent: due gestori di eventi

La chiamata a a.RaiseA()solo stampe "Alza A" - ma niente di più, perché il campo privato in A è nullo.

La chiamata a b.RaiseB() stampa le restanti tre righe, perché l'evento è stato sottoscritto due volte (una volta per stampare "Evento da A" e una volta per stampare "Evento da B").

Questo aiuto?

MODIFICA: per rendere più chiaro, pensare all'evento virtuale come a una coppia di metodi virtuali. È molto simile a questa:

public class A 
{ 
    private EventHandler handlerA; 

    public virtual void AddEventHandler(EventHandler handler) 
    { 
     handlerA += handler; 
    } 

    public virtual void RemoveEventHandler(EventHandler handler) 
    { 
     handlerA -= handler; 
    } 

    // RaiseA stuff 
} 

public class B : A 
{ 
    private EventHandler handlerB; 

    public override void AddEventHandler(EventHandler handler) 
    { 
     handlerB += handler; 
    } 

    public override void RemoveEventHandler(EventHandler handler) 
    { 
     handlerB -= handler; 
    } 

    // RaiseB stuff 
} 

Ora è più chiaro? Non è piuttosto così perché, per quanto ne so, non è possibile sovrascrivere solo la "parte" di un evento (ad esempio uno dei metodi) ma dà l'impressione generale corretta.

+0

Aiuta sicuramente, tranne che non è chiaro come a.VirtualEvent + = (s, e) => Console.WriteLine ("Evento da A"); è stato persino in grado di vedere B.VirtualEvent di iscriversi ad esso? – Prankster

+0

+1, bang! quello era il suono della mia mente che tornava indietro. Ho allungato il mio cervello fino al limite fissando il codice OP cercando di avvolgere il mio cervello intorno ad esso. Anche se avessi visto il bug, non sarei stato in grado di esprimerlo in modo così lucido. –

+0

B.VirtualEvent nasconde A.VirtualEvent, perché gli eventi non sono polimorfici. –

2

Hai collegato due gestori di eventi allo stesso evento. Poiché A e B puntano allo stesso oggetto, quando chiami b.RaiseB() vengono attivati ​​entrambi i gestori di eventi. Quindi per prima cosa chiami RaiseA che è un metodo di base. Questo stampa Raise A. Quindi in realtà non spara l'evento perché è nullo. Quindi, stai rilanciando B ma gli handler DUE sono collegati ad esso, quindi per prima cosa stampa Raise B, e quando l'evento si attiva, vengono chiamati entrambi i gestori.

0

Prova a rendere la funzione RaiseA protetta + virtuale.

Una regola pratica: se la classe derivata sostituisce gli accessor degli eventi, deve anche sovrascrivere la funzione che richiama l'evento.

+0

"Se la classe derivata sovrascrive gli accessor degli eventi, deve anche sovrascrivere la funzione che richiama l'evento.". Puoi indicarmi la sezione delle specifiche del linguaggio che lo afferma, per favore? – Prankster

+0

Dai un'occhiata a questa pagina: http://msdn.microsoft.com/en-us/library/8627sbea(VS.80).aspx –

+0

Esiste un'istruzione "Avvolgi l'evento in un metodo virtuale protetto per abilitare le classi derivate per sollevare l'evento. " –

Problemi correlati