C#:継承されたイベントを発生させる


144

次のイベントを含む基本クラスがあります。

public event EventHandler Loading;
public event EventHandler Finished;

この基本クラスを継承するクラスで、イベントを発生させようとします。

this.Loading(this, new EventHandler()); // All we care about is which object is loading.

次のエラーが表示されます。

イベント「BaseClass.Loading」は、+ =または-=(BaseClass ')の左側にのみ表示できます

他の継承メンバーと同じようにこれらのイベントにアクセスできないと思いますか?


この質問は、Frederik Gheyselsによって既に回答されています。Microsoft Docs:派生クラスで基本クラスイベントを発生させる方法(C#プログラミングガイド)を公​​式の "C#プログラミングガイド"として推奨
Eliahu Aaron

回答:


160

あなたがしなければならないことは、これです:

(イベントを宣言した)基本クラスで、イベントを発生させるために使用できる保護されたメソッドを作成します。

public class MyClass
{
   public event EventHandler Loading;
   public event EventHandler Finished;

   protected virtual void OnLoading(EventArgs e)
   {
       EventHandler handler = Loading;
       if( handler != null )
       {
           handler(this, e);
       }
   }

   protected virtual void OnFinished(EventArgs e)
   {
       EventHandler handler = Finished;
       if( handler != null )
       {
           handler(this, e);
       }
   }
}

(おそらく、イベントハンドラーを呼び出す必要があるかどうかを確認するために、これらのメソッドを変更する必要があります)。

次に、この基本クラスから継承するクラスで、OnFinishedまたはOnLoadingメソッドを呼び出すだけで、イベントを発生させることができます。

public AnotherClass : MyClass
{
    public void DoSomeStuff()
    {
        ...
        OnLoading(EventArgs.Empty);
        ...
        OnFinished(EventArgs.Empty);
    }
}

5
これらのメソッドは、特に理由がない限り、仮想的に保護する必要があります。
Max Schmeling、

6
それはなぜ仮想である必要があるのですか?イベントを発生させる方法を継承者に変更させたい場合は、仮想であると宣言しますが、ほとんどの場合、これを行う理由はありません...
Frederik Gheysels 2009


5
メソッドを仮想化することに関して、継承者がイベント呼び出しの動作をオーバーライドできるようにする必要があります。これが必要な状況で何回目ですか?その隣に; オーバーライドされたメソッドでは、TSによって言及されたものと同じエラーが発生するため、イベントを発生させることはできません。
Frederik Gheysels

2
@Verax推論が正しいので、私はそれに従います。コードの再利用可能性と拡張性に応じて、それを裏付けるための公式ガイドラインを提供しました。執筆時点では、.NET Veraxも非常に新しいようです。あなたが経験の私の7年間に反対するために、このアップを掘っていること少しややこしいですそれはそう
meandmycode

123

.NETはデリゲートを実際に保持する舞台裏でプライベートインスタンス変数を作成するため、宣言クラスのイベントにのみアクセスできます。これをやって…

public event EventHandler MyPropertyChanged;

実際にこれを行っています。

private EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

そしてこれをしています...

MyPropertyChanged(this, EventArgs.Empty);

これは...

myPropertyChangedDelegate(this, EventArgs.Empty);

したがって、(明らかに)宣言クラス内からのみプライベートデリゲートインスタンス変数にアクセスできます。

規約は、宣言クラスでこのようなものを提供することです。

protected virtual void OnMyPropertyChanged(EventArgs e)
{
    EventHandler invoker = MyPropertyChanged;

    if(invoker != null) invoker(this, e);
}

その後OnMyPropertyChanged(EventArgs.Empty)、そのクラス内の任意の場所または継承階層の下から呼び出して、イベントを呼び出すことができます。


これを実装する際にいくつかの問題がありました... stackoverflow.com/q/10593632/328397
goodguys_activate

アプローチだけでなく、アプローチを使用しなければならない理由を説明するので、私はこの回答を好みます。よくやった。
カウボーイダン2018

8

他の継承メンバーと同じようにこれらのイベントにアクセスできないと思いますか?

正確に。継承されたクラスからの発生を可能にするために、保護された関数を提供するOnXyzRaiseXyz、基本クラスの各イベントに慣例があります。例えば:

public event EventHandler Loading;

protected virtual void OnLoading() {
    EventHandler handler = Loading;
    if (handler != null)
        handler(this, EventArgs.Empty);
}

継承されたクラスで呼び出されます:

OnLoading();

0

あなたはこの方法を試すことができます、それは私のために働きます:

public delegate void MyEventHaldler(object sender, EventArgs e);

public class B
{
    public virtual event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

public class D : B
{
    public override event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

2
この記事は、仮想イベントを宣言し、それはコンパイラはこれを正しく処理しないと主張し、それらを上書きに対して警告していること注:msdn.microsoft.com/en-us/library/hy3sefw3.aspx
無線公衆

0

古いスレッドを復活させるのではなく、誰かが見ている場合に備えて、私がしたことは

protected EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

これにより、派生クラスでイベントを継承できるため、+ =構文を維持しながらメソッドをラップする必要なくイベントを呼び出すことができます。もしあなたがそうしたなら、あなたはまだラッピングメソッドでそれを行うことができると思います

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.