我可以依靠 base 类 的事件执行顺序来避免不必要的虚拟成员吗?

Can I rely on event execution order for base classes to avoid unnecessary virtual members?

有时我希望派生 class 对某些基础 class 事件做出反应并在通知任何其他订阅者之前更改状态。

我是否需要为每个事件复制 protected virtual void HandleXBeforeOthers(...),或者我可以依赖这样的事件执行顺序吗?

public class BaseClass
{
    public event X EventA;
    void RaiseA(...)
    {
        if (EventA != null) EventA(this, ...);
    }

    ...
}

public class Derived : BaseClass
{
    public Derived()
    {
        EventA += ...
    }
}

protected virtual”方法是:

public class BaseClass
{
    public event X EventA;
    void RaiseA(...)
    {
        HandleEventABeforeOthersCan(...);
        if (EventA != null) EventA(this, ...);
    }

    protected virtual void HandleEventABeforeOthersCan(...)
    {
        ...
    }

    ...
}

public class Derived : BaseClass
{
    protected override void HandleEventABeforeOthersCan(...)
    {
        ...
    }
}

答案取决于你想要什么样的保证。

构造函数在字段初始值设定项之后执行,基础 class 构造函数在派生 class 构造函数之前执行。因此,正如您自己的 Derived class 在其构造函数中向事件添加订阅者一样,基础 class 也可以在您之前完成此操作。

更糟糕的是,虽然它肯定不受欢迎,但没有什么可以阻止基础 class 构造函数将 this 传递给其他代码,在那里它有机会订阅在 Derived class 之前参加活动。

最后,只有实现事件的 class 才能完全控制事件处理程序的实际执行顺序。 通常,它只会调用表示事件的委托实例,它会按照订阅的顺序执行每个处理程序。但是没有什么可以阻止事件的实现者以其他方式维护和使用事件订阅。

例如它可以显式实现事件并在 add 方法中执行类似 eventField = value + eventField; 的操作(在订阅时反转订阅),或者在引发事件时显式枚举事件委托对象的调用列表,并且以相反的顺序调用处理程序。

当然,所有这些情况都应该非常罕见,尤其是最后一个(真的很奇怪)。偏离正常事件实现的代码是而且应该是非常罕见的。但是你能保证代码不是那样的吗?并非没有编写代码的人的承诺(即使在那里,人们也会犯错,可能会忘记或不小心违反他们的承诺)。

因此,如果您想要接近铁定的保证您的代码将先于任何其他代码执行,您需要制作自己的 class sealed(这样就没有其他人可以覆盖事件引发方法),并在调用基本 class 方法实现之前通过覆盖实际引发事件的虚拟方法进行处理。

即使在那里,您也依赖于一个隐含的承诺,即不会通过任何其他机制引发该事件。但这与您将获得的保证差不多。