[英]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.