簡體   English   中英

為什么事件不能在派生類中以與C#中的基類相同的方式使用?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM