何时应该重写 OnEvent 而不是在继承时订阅事件?

15

什么时候应该执行以下操作?

class Foo : Control
{
    protected override void OnClick(EventArgs e)
    {
        // new code here
    }
}

与此相反?

class Foo : Control
{
    public Foo()
    {
        this.Click += new EventHandler(Clicked);
    }

    private void Clicked(object sender, EventArgs e)
    {
        // code
    }
}
7个回答

10
覆盖而不是附加委托会导致更高效的代码,因此通常建议您在可能的情况下始终这样做。有关更多信息,请参见此MSDN文章。以下是相关引用:
  

受保护的OnEventName方法还允许派生类覆盖事件而无需将委托附加到其中。派生类必须始终调用基类的OnEventName方法,以确保已注册的委托接收事件。


有趣的是,设计指南规定,在创建虚拟OnFoo()方法时,即使下降类不调用base.OnFoo(),相应的Foo事件也应该被触发!不过在现实中实现这一点可能会很尴尬。 - Matt Hamilton
@Matt: private void RaiseFoo() { /* 调用派生类的 OnFoo / this.OnFoo( ); / 引发 foo 事件(即使基类的 OnFoo 没有被调用也能正常工作) / ... } protected virtual void OnFoo() { / 什么也不做 */ } - Emperor XLII

8

这个事件是为外部订阅者而设的。当你派生一些控件时,总是要重写OnEvent方法而不是订阅事件。这样,你可以确定你的代码被调用的时间,因为实际的事件是在你调用base.OnEvent()时触发的,而且你可以在你的代码之前、之后、中间或根本不调用它。然后,你还可以对事件的返回值(即EventArgs对象中的更改属性)做出反应。


+1 这正是原因。您可以确保代码在与消费者订阅的事件处理程序相关的时间运行的准确性。 - Noldorin

4

注意,在.NET 2.0中,我发现一些框架中的地方(特别是在DataTable类中),当对应的Foo事件被处理时,OnFoo方法仅会被调用!这违反了框架设计指南,但我们必须面对它。

我通过在类的某处使用虚拟处理程序来处理事件来解决了这个问题,例如:

public class MyDataTable : DataTable
{
    public override void EndInit()
    {
        base.EndInit();
        this.TableNewRow += delegate(object sender, DataTableNewRowEventArgs e) { };
    }

    protected override void OnTableNewRow(DataTableNewRowEventArgs e)
    {
        base.OnTableNewRow(e);
        // your code here
    }
}

这很有趣,我不知道那个。可能编写代码就像那样,这很可怕... - MagicKat

1

订阅事件是为了控制一个控件在另一个控件上监视事件。对于监视自己的事件OnClick是没问题的。然而,需要注意的是Control.OnClick处理触发那些订阅的事件,所以一定要在你的重写方法中调用它。


0

0
如果你像Kent Boogaart评论中所说的那样进行重写,你需要小心地调用base.OnClick来允许事件订阅被调用。

0

一个继承类不应该订阅它自己的事件,或者它基类的事件。

现在,如果一个类里有另一个不同的类的实例,那么它可以消耗那个类的事件,并确定是否应该触发自己的事件。

例如,最近我发布了一个 MRU List 类。在其中,有许多 ToolStripMenuItem 控件,我消耗了它们的点击事件。在那个点击事件被消耗后,我就触发了我的类的事件。(在这里查看源代码


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接