web-dev-qa-db-ja.com

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

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

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 '))の左側にのみ表示できます

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

138
jwarzech

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

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

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);
    }
}
149

.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)を呼び出して、イベントを呼び出すことができます。

121
Adam Robinson

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

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

public event EventHandler Loading;

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

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

OnLoading();
8
Konrad Rudolph

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

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);
    }
}
0

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

protected EventHandler myPropertyChangedDelegate;

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

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

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
0
Zeraphil