C #: gerando um evento herdado

144

Eu tenho uma classe base que contém os seguintes eventos:

public event EventHandler Loading;
public event EventHandler Finished;

Em uma classe que herda dessa classe base, tento gerar o evento:

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

Eu recebo o seguinte erro:

O evento 'BaseClass.Loading' pode aparecer apenas no lado esquerdo de + = ou - = (BaseClass ')

Estou assumindo que não posso acessar esses eventos da mesma forma que outros membros herdados?

jwarzech
fonte
Esta pergunta já foi respondida por Frederik Gheysels . A mesma prática é recomendada no Microsoft Docs: Como gerar eventos de classe base em classes derivadas (Guia de Programação em C #) como um "Guia de Programação em C # oficial"
Eliahu Aaron

Respostas:

160

O que você precisa fazer é o seguinte:

Na sua classe base (onde você declarou os eventos), crie métodos protegidos que podem ser usados ​​para gerar os 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);
       }
   }
}

(Observe que você provavelmente deve alterar esses métodos, a fim de verificar se é necessário chamar o manipulador de eventos ou não).

Em seguida, nas classes herdadas dessa classe base, você pode chamar os métodos OnFinished ou OnLoading para gerar os eventos:

public AnotherClass : MyClass
{
    public void DoSomeStuff()
    {
        ...
        OnLoading(EventArgs.Empty);
        ...
        OnFinished(EventArgs.Empty);
    }
}
Frederik Gheysels
fonte
5
Esses métodos devem ser protegidos virtuais, a menos que haja algum motivo para fazer o contrário.
Max Schmeling
6
Por que deveria ser virtual? Gostaria de declarar que virtual, se eu queria herdeiros para mudar a forma como o evento deve ser elevado, mas na maioria das vezes, não vejo nenhuma razão para fazer isso ...
Frederik Gheysels
4
Diretrizes oficiais: msdn.microsoft.com/en-us/library/w369ty8x(VS.80).aspx
meandmycode
5
Em relação à virtualização do método, para que os herdeiros possam substituir o comportamento de invocação de eventos: Quantas vezes você já esteve em uma situação em que isso era necessário? Ao lado disso; no método substituído, você não pode gerar o evento, pois receberá o mesmo erro que o mencionado por TS.
Frederik Gheysels 16/04/2009
2
@Verax, eu sigo isso porque o raciocínio é sólido, dependendo de quão reutilizável e extensível eu espero que o código seja, eu forneci diretrizes oficiais para fazer o backup .. no momento da redação, você também parece muito novo no .NET Verax por isso é um pouco desconcertante que você cavou isto para discordar com os meus 7 anos de experiência
meandmycode
123

Você pode acessar apenas um evento na classe declarante, pois o .NET cria variáveis ​​de instância privadas nos bastidores que realmente mantêm o delegado. Fazendo isso..

public event EventHandler MyPropertyChanged;

está realmente fazendo isso;

private EventHandler myPropertyChangedDelegate;

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

e fazendo isso ...

MyPropertyChanged(this, EventArgs.Empty);

é realmente isso ...

myPropertyChangedDelegate(this, EventArgs.Empty);

Portanto, você pode (obviamente) acessar apenas a variável de instância delegada privada de dentro da classe declarante.

A convenção é fornecer algo assim na classe declarante.

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

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

Você pode ligar OnMyPropertyChanged(EventArgs.Empty)de qualquer lugar nessa classe ou abaixo da hierarquia de herança para invocar o evento.

Adam Robinson
fonte
Eu tive alguns problemas de execução do presente ... stackoverflow.com/q/10593632/328397
goodguys_activate
Prefiro essa resposta, pois explica POR QUE você deve usar a abordagem em vez de apenas a abordagem. Bom trabalho.
31518 cowboydan
8

Estou assumindo que não posso acessar esses eventos da mesma forma que outros membros herdados?

Precisamente. É habitual fornecer uma função protegida OnXyzou RaiseXyzpara cada evento na classe base para ativar a geração de classes herdadas. Por exemplo:

public event EventHandler Loading;

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

Chamado na classe herdada:

OnLoading();
Konrad Rudolph
fonte
0

Você pode tentar dessa maneira, funciona para mim:

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);
    }
}
Olioul Islam Rahi
fonte
2
Observe que este artigo adverte contra declarar eventos virtuais e substituí-los, pois afirma que o compilador não lida com isso corretamente: msdn.microsoft.com/en-us/library/hy3sefw3.aspx
public wireless
0

não para ressuscitar um tópico antigo, mas no caso de alguém estar olhando, o que eu fiz foi

protected EventHandler myPropertyChangedDelegate;

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

Isso permite herdar o evento em uma classe derivada, para que você possa invocá-lo sem exigir que o método seja encapsulado, mantendo a sintaxe + =. Eu acho que você ainda pode fazer isso com os métodos de embalagem, se você fez

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
Zeraphil
fonte