为什么事件不能在派生类中以与C#中的基类相同的方式使用?

在下面的代码中,我想通过派生/子类化它来扩展类的行为,并使用基类的事件:

public class A { public event EventHandler SomeEvent; public void someMethod() { if(SomeEvent != null) SomeEvent(this, someArgs); } } public class B : A { public void someOtherMethod() { if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible? //Error: The event 'SomeEvent' can only appear on the left hand side of += or -= //(except when used from within the type 'A') } } 

为什么不可能?

对于这种情况,常见的解决方案是什么?

这里的标准做法是在基类上有一个受保护的虚方法OnSomeEvent,然后在派生类中调用该方法。 此外,出于线程原因,您需要在检查null并调用它之前保留对处理程序的引用。

有关读取Jon Skeet的答案或C#规范的原因的解释,该规范描述了编译器如何自动创建私有字段。

这是一个可能的工作。

 public class A { public event EventHandler SomeEvent; public void someMethod() { OnSomeEvent(); } protected void OnSomeEvent() { EventHandler handler = SomeEvent; if(handler != null) handler(this, someArgs); } } public class B : A { public void someOtherMethod() { OnSomeEvent(); } } 

编辑:根据框架设计指南第5.4节更新的代码和其他人的提醒 。

其他人已经解释了如何解决问题,但不是为什么它会出现。

声明类似公共字段的事件时,编译器会创建公共事件和私有字段。 在同一个类(或嵌套类)中,您可以直接访问该字段,例如调用所有处理程序。 从其他类中,您只能看到该事件,该事件仅允许订阅和取消订阅。

托德的回答是正确的。 通常,您会看到这在整个.NET框架中实现为OnXXX(EventArgs)方法:

 public class Foo { public event EventHandler Click; protected virtual void OnClick(EventArgs e) { var click = Click; if (click != null) click(this, e); } } 

我强烈建议您在发现自己使用各种CustomEventArgs / CustomEventHandler来引发事件之前考虑EventArgs / EventHandler模式 。

原始代码不起作用的原因是因为您需要访问事件的委托才能引发它,并且C#将此委托private

C#中的事件由一对方法add_SomeEventremove_SomeEvent公开表示,这就是为什么你可以从类外部订阅一个事件,但不提高它。

我的回答是你不应该这样做。

C#很好地执行只有声明/发布事件的类型才能触发/提升它。 如果基类受信任的派生具有提升其事件的能力,则创建者将公开受保护的方法来执行此操作。 如果它们不存在,那么它可能不应该这样做。

我设想的例子是,如果衍生类型被允许在他们的祖先中引发事件,世界会有多么不同。 注意:这不是有效的C#代码..(还是..)

 public class GoodVigilante { public event EventHandler LaunchMissiles; public void Evaluate() { Action a = DetermineCourseOfAction(); // method that evaluates every possible // non-violent solution before resorting to 'Unleashing the fury' if (null != a) { a.Do(); } else { if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); } } virtual protected string WhatsTheTime() { return DateTime.Now.ToString(); } .... } public class TriggerHappy : GoodVigilante { protected override string WhatsTheTime() { if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); } } // client code GoodVigilante a = new GoodVigilante(); a.LaunchMissiles += new EventHandler(FireAway); GoodVigilante b = new TriggerHappy(); // rogue/imposter b.LaunchMissiles += new EventHandler(FireAway); private void FireAway(object sender, EventArgs e) { // nuke 'em } 

用受保护的虚拟On …方法包装它:

 public class BaseClass { public event EventHandler SomeEvent; protected virtual void OnSomeEvent() { if(SomeEvent!= null) SomeEvent(this, new MyArgs(...) ); } } 

然后在派生类中重写它

 public class DerivedClass : BaseClass { protected override void OnSomeEvent() { //do something base.OnSomeEvent(); } } 

你将遍布.Net设置这种模式 – 所有表单和Web控件都遵循它。

不要使用前缀Raise … – 这与MS的标准不一致,并且可能在其他地方引起混淆。