C#:引发继承的事件


144

我有一个包含以下事件的基类:

public event EventHandler Loading;
public event EventHandler Finished;

在从该基类继承的类中,我尝试引发该事件:

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

我收到以下错误:

事件“ BaseClass.Loading”只能出现在+ =或-=(BaseClass')的左侧

我假设我无法像其他继承成员一样访问这些事件?


Frederik Gheysels已经回答了这个问题。建议在Microsoft Docs上使用相同的做法:如何在派生类中引发基类事件(C#编程指南),作为正式的“ C#编程指南”
Eliahu Aaron

Answers:


160

您要做的是:

在基类(已声明事件的位置)中,创建可用于引发事件的受保护方法:

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);
       }
   }
}

(请注意,您可能应该更改这些方法,以检查是否必须调用事件处理程序)。

然后,在从该基类继承的类中,您只需调用OnFinished或OnLoading方法来引发事件:

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

5
除非有其他理由,否则这些方法应受到虚拟保护。
Max Schmeling

6
为什么要虚拟?如果我想让继承者改变事件发生的方式,我会宣布它为虚拟的,但是大多数时候,我认为没有理由这样做……
Frederik Gheysels 09年


5
关于将方法虚拟化,以便继承者可以覆盖事件调用行为:在需要这样做的情况下,您经历了多少次?在那旁边;在重写的方法中,您不会引发该事件,因为您将得到与TS提到的错误相同的错误。
Frederik Gheysels 09年

2
@Verax我遵循它,因为推理是合理的,取决于我期望代码的可重用性和可扩展性,我提供了官方准则来支持这一点。在撰写本文时,.NET Verax似乎也很新因此,您可能会对此感到困惑,以致于不同意我的7年工作经验
meanmycode 2012年

123

您只能访问声明类中的事件,因为.NET在实际持有委托的幕后创建了私有实例变量。在做..

public event EventHandler MyPropertyChanged;

实际上正在这样做;

private EventHandler myPropertyChangedDelegate;

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

并这样做...

MyPropertyChanged(this, EventArgs.Empty);

其实是这个...

myPropertyChangedDelegate(this, EventArgs.Empty);

因此,您(显然)只能从声明类中访问私有委托实例变量。

约定是在声明类中提供类似的内容。

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

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

然后,您可以OnMyPropertyChanged(EventArgs.Empty)从该类中的任何位置或继承层次结构下的任何位置调用以调用事件。


我在执行此操作时遇到了一些问题... stackoverflow.com/q/10593632/328397
goodguys_activate 2012年

我更喜欢这个答案,因为它解释了为什么您必须使用方法而不是仅使用方法。辛苦了
cowboydan

8

我假设我无法像其他继承成员一样访问这些事件?

恰恰。通常,在基类中提供受保护的函数OnXyzRaiseXyz为每个事件提供保护,以允许从继承的类中引发事件。例如:

public event EventHandler Loading;

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

在继承的类中调用:

OnLoading();

0

您可以尝试这种方式,它对我有用:

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
请注意,本文警告您不要声明虚拟事件并覆盖它们,因为它声称编译器无法正确处理此事件:msdn.microsoft.com/en-us/library/hy3sefw3.aspx
公共无线

0

不要复活旧线程,但是如果有人在看,我所做的就是

protected EventHandler myPropertyChangedDelegate;

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

这使您可以在派生类中继承该事件,以便可以在保留+ =语法的同时无需包装方法就可以调用它。我想你仍然可以用包装方法做到这一点

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.