C #: generar un evento heredado


144

Tengo una clase base que contiene los siguientes eventos:

public event EventHandler Loading;
public event EventHandler Finished;

En una clase que hereda de esta clase base, trato de generar el evento:

this.Loading(this, new EventHandler()); // All we care about is which object is loading.

Recibo el siguiente error:

El evento 'BaseClass.Loading' solo puede aparecer en el lado izquierdo de + = o - = (BaseClass ')

¿Asumo que no puedo acceder a estos eventos de la misma manera que otros miembros heredados?


Esta pregunta ya fue respondida por Frederik Gheysels . Se recomienda la misma práctica en Microsoft Docs: Cómo generar eventos de clase base en clases derivadas (Guía de programación de C #) como una "Guía de programación de C #" oficial
Eliahu Aaron

Respuestas:


160

Lo que tienes que hacer es esto:

En su clase base (donde ha declarado los eventos), cree métodos protegidos que puedan usarse para generar los eventos:

public class MyClass
{
   public event EventHandler Loading;
   public event EventHandler Finished;

   protected virtual void OnLoading(EventArgs e)
   {
       EventHandler handler = Loading;
       if( handler != null )
       {
           handler(this, e);
       }
   }

   protected virtual void OnFinished(EventArgs e)
   {
       EventHandler handler = Finished;
       if( handler != null )
       {
           handler(this, e);
       }
   }
}

(Tenga en cuenta que probablemente debería cambiar esos métodos para verificar si tiene que invocar el controlador de eventos o no).

Luego, en las clases que heredan de esta clase base, puede llamar a los métodos OnFinished o OnLoading para generar los eventos:

public AnotherClass : MyClass
{
    public void DoSomeStuff()
    {
        ...
        OnLoading(EventArgs.Empty);
        ...
        OnFinished(EventArgs.Empty);
    }
}

55
Esos métodos deben protegerse virtuales a menos que haya alguna razón para hacerlo de otra manera.
Max Schmeling

66
¿Por qué debería ser virtual? Lo declararía virtual si quisiera que los herederos cambiaran la forma en que se debería
generar


55
Con respecto a hacer que el método sea virtual, para que los herederos puedan anular el comportamiento de invocación de eventos: ¿Cuántas veces ha estado en una situación en la que esto era necesario? Seguido de eso; en el método anulado, no puede generar el evento, ya que obtendrá el mismo error que el mencionado por TS.
Frederik Gheysels

2
@Verax Lo sigo porque el razonamiento es sólido, dependiendo de cuán reutilizable y extensible espero que sea el código, proporcioné pautas oficiales para respaldar eso ... al momento de escribir esto, también pareces muy nuevo en .NET Verax por lo que es un poco desconcertante que esta cavado hasta no estar de acuerdo con mis 7 años de experiencia
meandmycode

123

Solo puede acceder a un evento en la clase de declaración, ya que .NET crea variables de instancia privadas detrás de escena que realmente contienen al delegado. Haciendo esto..

public event EventHandler MyPropertyChanged;

en realidad está haciendo esto;

private EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

y haciendo esto ...

MyPropertyChanged(this, EventArgs.Empty);

es en realidad esto ...

myPropertyChangedDelegate(this, EventArgs.Empty);

Por lo tanto, (obviamente) solo puede acceder a la variable de instancia de delegado privado desde la clase de declaración.

La convención es proporcionar algo como esto en la clase de declaración.

protected virtual void OnMyPropertyChanged(EventArgs e)
{
    EventHandler invoker = MyPropertyChanged;

    if(invoker != null) invoker(this, e);
}

Luego puede llamar OnMyPropertyChanged(EventArgs.Empty)desde cualquier lugar de esa clase o debajo de la jerarquía de herencia para invocar el evento.


Tuve algunos problemas al implementar esto ... stackoverflow.com/q/10593632/328397
goodguys_activate

Prefiero esta respuesta ya que explica POR QUÉ tiene que usar el enfoque en lugar de solo el enfoque. Buen trabajo.
cowboydan

8

¿Asumo que no puedo acceder a estos eventos de la misma manera que otros miembros heredados?

Precisamente. Es habitual proporcionar una función protegida OnXyzo RaiseXyzpara cada evento en la clase base para permitir el aumento de clases heredadas. Por ejemplo:

public event EventHandler Loading;

protected virtual void OnLoading() {
    EventHandler handler = Loading;
    if (handler != null)
        handler(this, EventArgs.Empty);
}

Llamado en la clase heredada:

OnLoading();

0

Puedes probar de esta manera, funciona para mí:

public delegate void MyEventHaldler(object sender, EventArgs e);

public class B
{
    public virtual event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

public class D : B
{
    public override event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

2
Tenga en cuenta que este artículo advierte contra declarar eventos virtuales y anularlos, ya que afirma que el compilador no maneja esto correctamente: msdn.microsoft.com/en-us/library/hy3sefw3.aspx
inalámbrica pública

0

no para resucitar un hilo viejo pero en caso de que alguien esté mirando, lo que hice fue

protected EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

Esto le permite heredar el evento en una clase derivada para que pueda invocarlo sin necesidad de ajustar el método manteniendo la sintaxis + =. Supongo que aún podrías hacerlo con los métodos de envoltura si lo hicieras

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.