[英]Why events can't be used in the same way in derived classes as in the base class in 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<T>
/ EventHandler<T>
模式 。
原始代码不起作用的原因是因为您需要访问事件的委托才能引发它,并且C#将此委托private
。
C#中的事件由一对方法add_SomeEvent
和remove_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<MyArgs> 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的标准不一致,并且可能在其他地方引起混淆。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.