我有一个基类DockedToolWindow:Form,以及从DockedToolWindow派生的许多类。我有一个容器类,用于保存和分配事件到DockedToolWindow对象,但是我想调用子类中的事件。
我实际上有一个关于如何实现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);
}
}
当然这个例子编译并且有效,但是当我用“Move”(我从Form派生获得的事件)替换“ShapeChanged”时,它错误地说我没有+ =或 - =而没有移动右侧。我还删除了ShapeEventArgs通用标记。
任何煽动为什么这不起作用?在类中声明的事件与继承的事件之间有什么区别?
答案 0 :(得分:12)
您无法直接触发基类事件。这正是您必须使用OnShapeChanged
方法protected
而不是private
的原因。
改为使用base.OnMove()。
答案 1 :(得分:4)
从C#语言规范,第10.7节(增加重点):
在包含事件声明的类或结构的程序文本中,可以使用某些事件,如字段。要以这种方式使用,事件不能是抽象的或外部的,并且不能明确包含事件访问器声明。这样的事件可以在允许字段的任何上下文中使用。该字段包含一个委托(第15节),该委托引用已添加到事件的事件处理程序列表。如果未添加事件处理程序,则该字段包含null。
因此,您不能像处理字段一样处理Move事件的原因是它以不同的类型(在本例中为您的超类)定义。我同意@womp的猜测,即设计师做出了这个选择,以防止对事件进行无意识的捣蛋。允许不相关的类型(不是从声明事件的类型派生的类型)执行此操作似乎显然很糟糕,但即使对于派生类型,也可能不合适。他们可能必须包含语法以允许事件声明{field>} private
或protected
关于字段式使用,所以我猜他们选择完全禁止它。
答案 2 :(得分:3)
区别在于范围。在您的类中,您可以控制如何处理事件委托,但是,您的类无法控制基类正在执行的操作。它可能会对事件及其处理程序做一些疯狂的幕后操作。如果您只是“重新分配”Move事件,那么您将消除该事件的多播委托列表。
我猜他们对此进行了编译限制,因为它是一种非常不安全的做法,并且基本上会让任何后代类能够销毁其父级的事件模型。
答案 3 :(得分:1)
您只需要在定义事件本身的类中发布的代码。所有派生类应该直接调用OnShapeChanged()或OnMove()而不需要复制等,因此您不应该在类中编写该代码(因为Move事件在基础中定义)。
如果你确实需要在派生类中进行某种处理(也许你需要摆弄你的集合类?),你可以覆盖虚拟的OnXXX调用并在调用base.OnXXX()之前进行处理。在MSDN文章中,Circle类对应于DockedToolWindow类。您的派生类应该可以使用相同的模式。