【问题标题】:Raise Base Class Events in Derived Classes C#在派生类 C# 中引发基类事件
【发布时间】:2010-10-21 06:28:40
【问题描述】:

我有一个基类 DockedToolWindow : Form,以及许多从 DockedToolWindow 派生的类。我有一个容器类,它保存并将事件分配给 DockingToolWindow 对象,但是我想从子类调用事件。

我实际上对如何实现MSDN site 告诉我的操作有疑问。下面的这一部分给了我这个问题:

    // The event. Note that by using the generic EventHandler<T> event type
    // we do not need to declare a separate delegate type.
    public event EventHandler<ShapeEventArgs> ShapeChanged;

    public abstract void Draw();

    //The event-invoking method that derived classes can override.
    protected virtual void OnShapeChanged(ShapeEventArgs e)
    {
        // Make a temporary copy of the event to avoid possibility of
        // a race condition if the last subscriber unsubscribes
        // immediately after the null check and before the event is raised.
        EventHandler<ShapeEventArgs> handler = ShapeChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

当然,这个示例可以编译并且可以工作,但是当我将“ShapeChanged”替换为“Move”(我从 Form 派生的一个事件)时,它会出错,说如果没有 += 或 -=,我不能在右侧移动 Move。我还删除了 ShapeEventArgs 通用标签。

关于为什么这不起作用的任何煽动?在类中声明的事件和继承的事件有什么区别?

【问题讨论】:

    标签: c# events base-class


    【解决方案1】:

    区别在于范围。在您的类中,您可以控制事件委托的处理方式,但是,您的类无法控制基类在做什么。它可能正在对事件及其处理程序做一些疯狂的幕后工作。如果您只是简单地“重新分配” Move 事件,您将清除该事件的多播委托列表。

    我猜他们对此设置了编译器限制,因为这是一种非常不安全的做法,并且本质上会使任何后代类都能够破坏其父类的事件模型。

    【讨论】:

      【解决方案2】:

      来自 C# 语言规范,第 10.7 节(已添加重点):

      在包含事件声明的类或结构的程序文本中,某些事件可以像字段一样使用。要以这种方式使用,事件不能是抽象的或外部的,并且不能显式包含事件访问器声明。这样的事件可以在允许字段的任何上下文中使用。该字段包含一个委托(第 15 节),它引用已添加到事件的事件处理程序列表。如果未添加任何事件处理程序,则该字段包含 null。

      因此,您不能将 Move 事件视为字段的原因是它以不同的类型定义(在本例中为您的超类)。我同意@womp 的推测,即设计师做出这个选择是为了防止无意中对事件进行胡闹。允许不相关的类型(不是从声明事件的类型派生的类型)执行此操作似乎显然很糟糕,但即使对于派生类型,它也可能是不可取的。他们可能必须包含语法以允许在字段样式使用方面进行privateprotected 的事件声明,所以我的猜测是他们选择完全禁止它。

      【讨论】:

        【解决方案3】:

        您只需要在定义事件本身的类中发布的代码。所有派生类都应该直接调用 OnShapeChanged() 或 OnMove(),无需进行复制等操作,因此您根本不应该在类中编写该代码(因为 Move 事件是在基类中定义的)。

        如果您确实需要在派生类中进行某种处理(也许您需要摆弄您的集合类?),您可以覆盖虚拟 OnXXX 调用,并在调用 base.OnXXX() 之前进行处理。在 MSDN 文章中,Circle 类对应于您的 DockingToolWindow 类。您的派生类应该可以使用相同的模式。

        【讨论】:

        • 我看到 OnMove() 可以解决这个问题,但是我怎么不能在需要的时候显式调用 OnMove() 呢?我不知道框架实际上在哪里调用了 Move 委托,而且可能无论如何都对我隐藏了。
        【解决方案4】:

        您不能直接触发基类事件。这正是您必须将OnShapeChanged 方法设为protected 而不是private 的原因。

        请改用base.OnMove()

        【讨论】:

        • 我看到 OnMove() 可以解决这个问题,但是我怎么不能在需要时显式调用 OnMove() 可以吗?我不知道框架实际上在哪里调用了 Move 委托,而且可能无论如何都对我隐藏了。
        • OnMove 触发 Move 事件,就像 OnShapeChanged 在代码中触发 ShapeChanged 事件一样。添加触发事件的受保护成员以使它们对派生类可见是一种常见模式。在这种情况下,通常会添加“On”前缀(OnMove、OnClick 等)
        • 要查看框架实际触发 Move 事件的位置,请使用 Reflector 工具 (red-gate.com/products/reflector) 获取 Form.OnMove 方法的源代码。
        • [注意] Reflector 从此成为付费应用,因此(如果您想要免费应用),您需要查看ILSpy 或其他类似应用。
        • JustDecompile 来自 Telerik 是免费的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-02-06
        • 2017-11-16
        • 1970-01-01
        • 2019-01-17
        • 2012-01-24
        • 2018-05-19
        • 2015-07-23
        相关资源
        最近更新 更多