【问题标题】:How to correctly override method which raises an event如何正确覆盖引发事件的方法
【发布时间】:2012-07-11 00:19:56
【问题描述】:

我有一个 MyCol 类,它继承自 ObservableCollection(Of T)。它以这种方式覆盖 InsertItem 方法:

Public Event PreInsertItem As EventHandler(Of EventArgs)

Protected Overridable Sub OnPreInsertItem(e As EventAtgs)
    RaiseEvent PreInsertItem(Me, e)
End Sub

Protected Overrides Sub InsertItem(index As Integer, item As T)
    OnPreInsertItem(EventArgs.Empty)

    MyBase.InsertItem(index, item)
End Sub

如您所见,我添加了一个事件,每次将项目添加到 MyCol 集合时都会引发该事件。

接下来我创建另一个类 MyColSubClass,它继承自 MyCol,并且还覆盖了 InsertItem 方法:

Public Overrides Sub InsertItem(index as Integer, item as T)
    OnPreInsertItem(EventArgs.Empty)

    ' some additional code goes here

    MyBase.InsertItem(index, item)
End Sub

问题:

现在,当我使用 MyColSubClass 的实例并添加一个项目时,PreInsertItem 事件会引发两次:首先是在 MyColSubClass 中,然后是在 MyCol 中。

我应该使用什么设计模式来使 PreInsertItem 事件只引发一次:在 MyColSubClass 中?

注意

示例中显示的类和事件是从现实生活中的应用程序简化而来的,但假设它们显示了应用程序的确切结构。在最后一个继承的类中引发事件是必须的。

【问题讨论】:

  • 为什么要从子类调用OnPreInsertItem(EventArgs.Empty)?当您执行 MyBase.InsertItem(index, item) 时,它已经被调用了。
  • 这里我展示了一个简化的例子。在实际应用中,大量数据在事件数据中传递。所以必须在每个继承的类(即 MyColSubClass)中调用事件。

标签: .net vb.net events inheritance overriding


【解决方案1】:

如果我猜对了,您想在不同的继承级别上向 EventArg 类添加信息。在这种情况下,在我看来,提供与引发事件相同级别的 EventArg 成员是最好的解决方案,而 InsertItem() 的所有覆盖只是修改此 EventArg 成员,事件以及此 eventargs 仅在尽可能高的情况下引发级别。

【讨论】:

    【解决方案2】:

    如果您确定基类会引发事件,那么在派生类中这样做是没有用的。

    只需将您的覆盖更改为:

    Public Overrides Sub InsertItem(index as Integer, item as T)
        ' some additional code goes here
    
        MyBase.InsertItem(index, item)
    End Sub
    

    应该没问题。

    但是,如果您更改派生方法并停止调用 MyBase.InsertItem(...),您应该在覆盖中引发事件以确保它被引发:

    Public Overrides Sub InsertItem(index as Integer, item as T)
        ' some additional code goes here
    
        OnPreInsertItem(EventArgs.Empty)
    
        ' insert your item and do whatever...
    End Sub
    

    编辑

    如果您需要更改引发事件的方式,但想确保它只引发一次,只需在派生类中重写 OnPreInsertItem 方法:

    Protected Overrides Sub OnPreInsertItem(e as EventArgs)
        ' Do wahetever you need here, change e, add info, whatever...
        ' ...
        ' Then raise the event (or call MyBase.OnPreInsertItem, as you like)
        RaiseEvent PreInsertItem(Me, e)
    End Sub
    
    Public Overrides Sub InsertItem(index as Integer, item as T)
        ' some additional code goes here
    
        ' This will work only if MyBase.InsertItem calls OnPreInsertItem. 
        ' Otherwise, you have to handle the insertion and raise the event
        ' yourself without calling the base method.
        MyBase.InsertItem(index, item)
    End Sub
    

    由于 OnPreInsertItem 是可覆盖的,当您在派生类中插入项目时,将调用派生类的版本。

    希望有帮助:)

    【讨论】:

    • 我的问题中有一个简化的例子。在实际应用程序中,我必须在任何其他代码之前引发事件,因为事件会传递大量数据(同样,在实际应用程序中)。
    • 上次编辑中的出色解决方案!谢谢,这真的很有帮助,不知道我自己是怎么错过的:)
    • 如果我在你的基类 (MyCol) 中删除 MyBase.InsertItem 并没有完全错,那么该项目不会被添加到 ObservableCollection 中,这会使这个示例变得无用。
    • 你是对的,如果他从 MyCol 中删除了 MyBase.InsertItem。我很有信心他不会这样做,如果他这样做了,他会很快恢复他的改变:)
    • @T.Fabre 阅读不好,我的坏:) 仍然对 OP 在调用 OnPreInsertItem() 两次时试图实现的目标感到困惑。删除一个电话很明显。
    【解决方案3】:

    我认为你不应该在你的子类 MyColSubClass 中提出 OnPreInsertItem
    你的方法应该是这样的:

    Public Overrides Sub InsertItem(index as Integer, item as T)
        ' some additional code goes here
    
        MyBase.InsertItem(index, item)
    End Sub
    

    您正在扩展基类的某些功能。 如果您要替换该特定功能,那么您的方法应该是这样的:

    Public Overrides Sub InsertItem(index as Integer, item as T)
        OnPreInsertItem(EventArgs.Empty)
    
        ' some additional code goes here
    
    End Sub
    

    您可以找到更多信息herehere

    【讨论】:

      猜你喜欢
      • 2015-09-16
      • 1970-01-01
      • 1970-01-01
      • 2016-03-07
      • 2011-01-20
      • 1970-01-01
      • 2016-10-19
      • 2010-10-02
      • 1970-01-01
      相关资源
      最近更新 更多