为什么不能在派生类中以与 C# 中的基类中相同的方式使用事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/253757/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why events can't be used in the same way in derived classes as in the base class in C#?
提问by mr_georg
In following code, I want to extend the behaviour of a class by deriving/subclassing it, and make use of an event of the base class:
在以下代码中,我想通过派生/子类化类来扩展类的行为,并利用基类的事件:
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')
}
}
Why isn't it possible?
为什么不可能?
And what is the common solution for this kind of situation?
这种情况的常见解决方案是什么?
采纳答案by Todd White
The standard practice here is to have a protected virtual method OnSomeEvent on your base class, then call that method in derived classes. Also, for threading reasons you will want to keep a reference to the handler before checking null and calling it.
这里的标准做法是在您的基类上有一个受保护的虚拟方法 OnSomeEvent,然后在派生类中调用该方法。此外,出于线程原因,您需要在检查 null 并调用它之前保留对处理程序的引用。
For an explanation of the why read Jon Skeet'sanswer or the C# specificationwhich describes how the compiler automatically creates a private field.
有关为什么阅读Jon Skeet 的回答或描述编译器如何自动创建私有字段的C# 规范的解释。
Here is one possible work around.
这是一种可能的解决方法。
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();
}
}
Edit:Updated code based upon Framework Design Guidelines section 5.4and remindersby others.
编辑:根据框架设计指南第 5.4 节和其他人的提醒更新了代码。
回答by Keith
Wrap it with a protected virtual On... method:
用受保护的虚拟 On... 方法包装它:
public class BaseClass
{
public event EventHandler<MyArgs> SomeEvent;
protected virtual void OnSomeEvent()
{
if(SomeEvent!= null)
SomeEvent(this, new MyArgs(...) );
}
}
Then override this in a derived class
然后在派生类中覆盖它
public class DerivedClass : BaseClass
{
protected override void OnSomeEvent()
{
//do something
base.OnSomeEvent();
}
}
You'll set this pattern all over .Net - all form and web controls follow it.
您将在整个 .Net 上设置此模式 - 所有表单和 Web 控件都遵循它。
Do not use the prefix Raise... - this is not consistent with MS's standards and can cause confusion elsewhere.
不要使用前缀 Raise... - 这与 MS 的标准不一致,可能会在其他地方引起混淆。
回答by Jon Skeet
Others have explained how to get round the issue, but not why it's coming up.
其他人已经解释了如何解决这个问题,但没有解释为什么会出现。
When you declare a public field-like event, the compiler creates a public event, and a private field. Within the same class (or nestedclasses) you can get at the field directly, e.g. to invoke all the handlers. From other classes, you only see the event, which only allows subscription and unsubscription.
当您声明一个类似公共字段的事件时,编译器会创建一个公共事件和一个私有字段。在同一个类(或嵌套类)中,您可以直接获取该字段,例如调用所有处理程序。从其他类中,您只能看到事件,该事件只允许订阅和取消订阅。
回答by cfeduke
Todd's answer is correct. Often you will see this implemented throughout the .NET framework as OnXXX(EventArgs)
methods:
托德的回答是正确的。通常,您会看到在整个 .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);
}
}
I strongly encourage you to consider the EventArgs<T>
/EventHandler<T>
patternbefore you find yourself making all manner of CustomEventArgs
/CustomEventHandler
for raising events.
我强烈建议您在发现自己制作各种/以引发事件之前考虑EventArgs<T>
/EventHandler<T>
模式。CustomEventArgs
CustomEventHandler
回答by Tim Robinson
The reason the original code doesn't work is because you need to have access to the event's delegate in order to raise it, and C# keeps this delegate private
.
原始代码不起作用的原因是您需要访问事件的委托才能引发它,而 C# 保留此委托private
。
Events in C# are represented publicly by a pair of methods, add_SomeEvent
and remove_SomeEvent
, which is why you can subscribe to an event from outside the class, but not raise it.
C# 中的事件由一对方法公开表示,add_SomeEvent
and remove_SomeEvent
,这就是为什么您可以从类外部订阅事件,但不能引发它。
回答by Gishu
My answer would be that you shouldn't have to do this.
我的回答是你不应该这样做。
C# nicely enforces Only the type declaring/publishing the event should fire/raise it.If the base class trusted derivations to have the capability to raise its events, the creator would expose protected methods to do that. If they don't exist, its a good hint that you probably shouldn't do this.
C# 很好地强制执行只有声明/发布事件的类型应该触发/引发它。如果基类信任派生具有引发其事件的能力,则创建者将公开受保护的方法来执行此操作。如果它们不存在,这是一个很好的提示,您可能不应该这样做。
My contrived example as to how different the world would be if derived types were allowed to raise events in their ancestors. Note: this is notvalid C# code.. (yet..)
关于如果允许派生类型在其祖先中引发事件,世界将有多么不同的我人为的例子。注意:这不是有效的 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
}