web-dev-qa-db-ja.com

C#で、イベントハンドラーが定義されているクラスの外部のどこかでnullかどうかをテストできないのはなぜですか?

C#のイベントやデリゲートに関する基本的なことを理解していないと確信していますが、このコードサンプルでブールテストを実行できないのはなぜですか。

public class UseSomeEventBase {
    public delegate void SomeEventHandler(object sender, EventArgs e);
    public event SomeEventHandler SomeEvent;
    protected void OnSomeEvent(EventArgs e) {
        // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
        if (SomeEvent != null) SomeEvent(this, e);
    }
}

public class UseSomeEvent : UseSomeEventBase {
    public bool IsSomeEventHandlerNull() {
        // "LEFT HAND SIDE" COMPILER ERROR
        return SomeEvent == null;
    }
}

class Program {
    static void Main(string[] args) {
        var useSomeEvent = new UseSomeEvent();
        useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEvent.SomeEvent == null) {

        }
        var useSomeEventBase = new UseSomeEventBase();
        useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
        // "LEFT HAND SIDE" COMPILER ERROR
        if (useSomeEventBase.SomeEvent == null) {

        }
    }

    static void FuncToHandle(object sender, EventArgs e) { }
}
34
gabe

イベントは、実際には単なる「追加」操作と「削除」操作です。値を取得することも、値を設定することも、呼び出すこともできません。イベントのハンドラーをサブスクライブする(add)か、サブスクライブ解除する(remove )。これは問題ありません-カプセル化されており、プレーンでシンプルです。追加/削除を適切に実装するかどうかはパブリッシャー次第ですが、パブリッシャーが詳細を使用可能にすることを選択しない限り、サブスクライバーは実装固有の部分を変更またはアクセスできません。

C#のフィールドのようなイベント(追加/削除ビットを指定しない場合)これを非表示にします-デリゲート型の変数を作成しますandイベント。イベントの追加/削除の実装では、変数を使用してサブスクライバーを追跡します。

クラス内では変数を参照し(現在サブスクライブされているデリゲートを取得して実行できるようにします)、クラスの外ではイベント自体を参照します(追加/削除機能しかありません)。

フィールドのようなイベントの代わりに、追加/削除を明示的に実装することもできます。

private EventHandler clickHandler; // Normal private field

public event EventHandler Click
{
    add
    {
        Console.WriteLine("New subscriber");
        clickHandler += value;
    }
    remove
    {
        Console.WriteLine("Lost a subscriber");
        clickHandler -= value;
    }
}

詳細は イベントに関する私の記事 を参照してください。

もちろん、イベントパブリッシャーcanは、より多くの情報を利用可能にします-ClickHandlersのようなプロパティを記述して、現在のマルチキャストデリゲートを返すことができます、またはHasClickHandlerstoが存在するかどうかを返します。ただし、これはコアイベントモデルの一部ではありません。

59
Jon Skeet

ここで非常にシンプルなアプローチを使用して、イベントを繰り返しサブスクライブしないようにすることができます。

以下の2つの方法のいずれかを使用できます。

  1. フラグアプローチ:_getWarehouseForVendorCompletedSubscribeは、falseに初期化されたプライベート変数です。

        if (!_getWarehouseForVendorCompletedSubscribed)
        {
            _serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
            _getWarehouseForVendorCompletedSubscribed = true;
        }
    
  2. 購読解除のアプローチ:購読するたびに購読解除を含めます。

    _serviceClient.GetWarehouseForVendorCompleted -= new 
        EventHandler<GetWarehouseForVendorCompletedEventArgs>  
     (_serviceClient_GetWarehouseForVendorCompleted);
    
    
    _serviceClient.GetWarehouseForVendorCompleted += new 
           EventHandler<GetWarehouseForVendorCompletedEventArgs>
           (_serviceClient_GetWarehouseForVendorCompleted);
    
16
Sunil

ここに答え:

using System;
delegate void MyEventHandler();
class MyEvent
{
    string s;
    public event MyEventHandler SomeEvent;
    // This is called to raise the event.
    public void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            SomeEvent();
        }

    }

    public string IsNull
    {
        get
        {
            if (SomeEvent != null)
                 return s = "The EventHandlerList is not NULL";
            else return s = "The EventHandlerList is NULL"; ;
        }
    }
}

class EventDemo
{  
    // An event handler.
    static void Handler()
    {
       Console.WriteLine("Event occurred");
    }

    static void Main()
    {
       MyEvent evt = new MyEvent();
       // Add Handler() to the event list.
       evt.SomeEvent += Handler;
       // Raise the event.
       //evt.OnSomeEvent();
       evt.SomeEvent -= Handler;
       Console.WriteLine(evt.IsNull);
       Console.ReadKey();
   }
} 
3
Tom

これは少し異なる質問です

外部で定義されたイベントのnullのテストにはどのような価値がありますか?

イベントの外部コンシューマーとして実行できる操作は2つだけです

  • ハンドラーを追加する
  • ハンドラーを削除する

イベントのnullまたはnon-nullnessは、これらの2つのアクションには影響しません。知覚できる価値のないテストを実行したいのはなぜですか?

2
JaredPar

これは、「イベント」キーワードを使用するときに適用されるルールです。イベントを作成すると、デリゲートとの外部クラスの相互作用が「サブスクライブ/サブスクライブ解除」関係に制限されます。これには、継承のケースが含まれます。イベントは本質的にプロパティですが、メソッド呼び出しの場合、それは実際にはオブジェクト自体ではないため、実際には次のようになります。

public event SomeEventHandler SomeEvent
{
     add
     {
          //Add method call to delegate
     }
     remove
     {
          //Remove method call to delegate
     }
}
1
Zensar

イベントのパブリッシャーは暗黙的にオーバーロードのみ+=および-=操作、および他の操作はパブリッシャーに実装されていません。これは、上で説明したように、サブスクライバーに変更イベントを制御させたくないなどの明らかな理由によります。

特定のイベントがサブスクライバークラスでサブスクライブされているかどうかを検証する場合、より優れたパブリッシャーは、イベントがサブスクライバーである場合にクラスにフラグを設定し、サブスクライバーでない場合はフラグをクリアします。

サブスクライバーがパブリッシャーのフラグにアクセスできる場合、フラグの値をチェックすることにより、特定のイベントがサブスクライバーかどうかを非常に簡単に識別できます。

0
user3202543

基本クラスからそれを行う必要があります。それがあなたがこれをした正確な理由です:

protected void OnSomeEvent(EventArgs e) {
    // CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
    if (SomeEvent != null) SomeEvent(this, e);
}

派生クラスからイベントにアクセスすることはできません。また、そのメソッドを仮想化して、派生クラスでオーバーライドできるようにする必要があります。

0
Darthg8r