2009-04-03 11 views
9

A continuación se muestra el programa que utilicé para la prueba. Imprime (como se esperaba):¿Cómo funcionan los eventos virtuales en C#?

Raise A 
Event from A 
Raise B 
Event from B 

Ahora, si cambiamos dos primeras líneas del principal a ser:

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

el programa imprimirá:

Raise A 
Raise B 
Event from B 

cual es también esperado, ya que el evento dominante oculta el campo de respaldo privado en la clase base y, por lo tanto, los eventos activados por la clase base no son visibles para los clientes de la clase derivada.

Ahora estoy cambiando las mismas líneas a:

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

y el programa comienza la impresión:

Raise A 
Raise B 
Event from A 
Event from B 

¿Qué está pasando?

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

Artículo "Eventos virtuales en C#: algo salió mal" - http://www.viva64.com/es/b/0453/ –

Respuesta

12

Tenemos una sola instancia (de B), que tiene los siguientes campos:

  • A.VirtualEvent: null
  • B.VirtualEvent: Dos controladores de eventos

La llamada a a.RaiseA()solo imprime "Elevar A" - pero nada más, porque el campo privado en A es nulo.

La llamada a b.RaiseB() imprime las tres líneas restantes, porque el evento se ha suscrito a dos veces (una para imprimir "Evento desde A" y una para imprimir "Evento desde B").

¿Eso ayuda?

EDITAR: Para hacerlo más claro, piense en el evento virtual como un par de métodos virtuales. Es muy parecido a esto:

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 
} 

¿Ahora está más claro? No es bastante así porque, por lo que yo sé, no puede anular solo una "parte" de un evento (es decir, uno de los métodos) pero da la impresión general correcta.

+0

Definitivamente ayuda, excepto que no está claro cómo a.VirtualEvent + = (s, e) => Console.WriteLine ("Evento desde A"); incluso fue capaz de ver B.VirtualEvent para suscribirse a él? – Prankster

+0

+1, ¡bang! ese fue el sonido de mi mente respondiendo. Extendí mi cerebro hasta el límite mirando el código de OP tratando de envolver mi cerebro alrededor de él. Incluso si viera el error, no habría podido decirlo tan lúcidamente. –

+0

B.VirtualEvent oculta A.VirtualEvent, porque los eventos no son polimórficos. –

2

Conectó dos controladores de eventos al mismo evento. Como A y B apuntan al mismo objeto, cuando llamas a b.RaiseB() ambos controladores de eventos son despedidos. Entonces primero llamas a RaiseA, que es un método de basecals. Eso imprime Raise A. No dispara el evento porque es nulo. Entonces, está levantando B pero DOS manipuladores están conectados a él, por lo tanto primero imprime Raise B, y cuando el evento se dispara, se llama a ambos manejadores.

0

Intente hacer que su función RaiseA esté protegida + virtual.

Una regla general: si la clase derivada anula los accesos al evento, también debe anular la función que invoca el evento.

+0

"Si la clase derivada anula los accesos al evento, también debe anular la función que invoca el evento". ¿Puede indicarme la sección de especificación del lenguaje que dice esto, por favor? – Prankster

+0

Eche un vistazo a esta página: http://msdn.microsoft.com/en-us/library/8627sbea(VS.80).aspx –

+0

Hay una declaración "Ajustar el evento en un método virtual protegido para habilitar clases derivadas para levantar el evento ". –

Cuestiones relacionadas