サーチ…


前書き

イベントとは、何かが発生したこと(マウスクリックなど)や、場合によっては(価格変更など)起こりそうなことの通知です。

クラスはイベントを定義することができ、そのインスタンス(オブジェクト)はこれらのイベントを発生させることがあります。たとえば、Buttonには、ユーザーがクリックしたときに発生するClickイベントが含まれている場合があります。

イベントハンドラは、対応するイベントが発生したときに呼び出されるメソッドです。フォームには、ボタンに含まれるすべてのボタンのClickedイベントハンドラが含まれています。

パラメーター

パラメータ詳細
EventArgsT EventArgsから派生し、イベントパラメータを含む型。
イベント名イベントの名前。
ハンドラー名イベントハンドラの名前。
SenderObject イベントを呼び出すオブジェクト。
EventArguments イベントパラメータを含むEventArgsT型のインスタンス。

備考

イベントを起こすとき:

  • デリゲートがnullかどうかを常にチェックしnull 。 nullデリゲートは、イベントにサブスクライバがないことを意味します。サブスクライバのないイベントを発生させると、 NullReferenceExceptionが発生します。
6.0
  • nullを確認する前に、デリゲート(eg EventName )をローカル変数( eventName )にコピーして、イベントを発生させます。これにより、マルチスレッド環境での競合状態が回避されます。

間違っている

    if(Changed != null)      // Changed has 1 subscriber at this point
                             // In another thread, that one subscriber decided to unsubscribe
        Changed(this, args); // `Changed` is now null, `NullReferenceException` is thrown.

    // Cache the "Changed" event as a local. If it is not null, then use
    // the LOCAL variable (handler) to raise the event, NOT the event itself.
    var handler = Changed;
    if(handler != null)
        handler(this, args);
6.0
  • ifステートメント内のサブスクライバのヌルチェックではなく、ヌル条件付き演算子(?)を使用してメソッドをEventName?.Invoke(SenderObject, new EventArgsT());EventName?.Invoke(SenderObject, new EventArgsT());
  • Action <>を使用してデリゲート型を宣言する場合、匿名メソッド/イベントハンドラのシグネチャは、イベント宣言の宣言された匿名デリゲート型と同じである必要があります。

イベントの宣言と募集

イベントの宣言

次の構文を使用して、任意のclassまたはstructイベントを宣言できます。

public class MyClass
{
    // Declares the event for MyClass
    public event EventHandler MyEvent;

    // Raises the MyEvent event
    public void RaiseEvent()
    {
        OnMyEvent();
    }
}    

イベントの宣言のための拡張構文があり、イベントのプライベートインスタンスを保持し、 addおよびsetアクセサを使用してパブリックインスタンスを定義します。構文はC#のプロパティと非常によく似ています。コンパイラは、複数のスレッドがクラスのイベントにイベントハンドラを安全に追加および削除できるようにするコードを生成するため、上記の構文を優先する必要があります。

イベントの開催

6.0
private void OnMyEvent()
{
    EventName?.Invoke(this, EventArgs.Empty); 
}
6.0
private void OnMyEvent()
{
    // Use a local for EventName, because another thread can modify the
    // public EventName between when we check it for null, and when we
    // raise the event.
    var eventName = EventName;

    // If eventName == null, then it means there are no event-subscribers,
    // and therefore, we cannot raise the event.
    if(eventName != null)
        eventName(this, EventArgs.Empty);

}

イベントは、宣言型によってのみ呼び出すことができます。クライアントは購読/購読解除しかできません。

EventName?.Invokeがサポートされていない6.0以前のC#バージョンでは、この例に示すように、起動前に一時変数にイベントを割り当てて、複数のスレッドが同じものを実行する場合にスレッドの安全を確保することをおEventName?.Invokeコード。そうしないと、複数のスレッドが同じオブジェクトインスタンスを使用している特定の状況で、 NullReferenceExceptionがスローされることがあります。 C#6.0では、コンパイラはC#6のコード例に示すようなコードを出力します。

標準イベント宣言

イベント宣言:

public event EventHandler<EventArgsT> EventName;

イベントハンドラの宣言:

public void HandlerName(object sender, EventArgsT args) { /* Handler logic */ }

イベントを購読する:

動的に:

EventName += HandlerName;

デザイナーを通して:

  1. コントロールのプロパティウィンドウ(ライトニングボルト)の[イベント]ボタンをクリックします。
  2. イベント名をダブルクリックします:

ここに画像の説明を入力

  1. Visual Studioはイベントコードを生成します:
private void Form1_Load(object sender, EventArgs e)
{

}

メソッドを呼び出す:

EventName(SenderObject, EventArguments);

匿名イベントハンドラ宣言

イベント宣言:

public event EventHandler<EventArgsType> EventName;

ラムダ演算子=>を使用してイベントに登録するイベントハンドラの宣言:

EventName += (obj, eventArgs) => { /* Handler logic */ };

デリゲート匿名メソッド構文を使用したイベントハンドラ宣言:

EventName += delegate(object obj, EventArgsType eventArgs) { /* Handler Logic */ };

イベントのパラメータを使用しないイベントハンドラの宣言とサブスクリプションです。したがって、パラメータを指定することなく上記の構文を使用できます。

EventName += delegate { /* Handler Logic */ }

イベントを呼び出す:

EventName?.Invoke(SenderObject, EventArguments);

非標準イベント宣言

イベントは、 EventHandlerおよびEventHandler<T>だけでなく、任意のデリゲート型にすることができます。例えば:

//Declaring an event
public event Action<Param1Type, Param2Type, ...> EventName;

これは、標準のEventHandlerイベントと同様に使用されます。

//Adding a named event handler
public void HandlerName(Param1Type parameter1, Param2Type parameter2, ...) {
    /* Handler logic */
}
EventName += HandlerName;

//Adding an anonymous event handler
EventName += (parameter1, parameter2, ...) => { /* Handler Logic */ };

//Invoking the event
EventName(parameter1, parameter2, ...);

同じタイプの複数のイベントをフィールドやローカル変数と同様に1つのステートメントに宣言することは可能です(ただし、これはしばしば悪い考えです)。

public event EventHandler Event1, Event2, Event3;

これは、 EventHandler型の3つの別々のイベント( Event1Event2 、およびEvent3 )を宣言します。
注意:いくつかのコンパイラはインタフェースとクラスでこの構文を受け入れるかもしれませんが、C#の仕様(v5.0§13.2.3)はインタフェースを許可しないインタフェースの文法を提供しているため、インタフェースでの使用は異なるコンパイラでは信頼できません。

追加データを含むカスタムEventArgを作成する

カスタムイベントは通常、イベントに関する情報を含むカスタムイベント引数を必要とします。たとえば、 MouseDownイベントやMouseUpイベントなどのマウスイベントで使用されるMouseEventArgsには、イベントの生成に使用されたLocationまたはButtonsに関する情報が含まれています。

新しいイベントを作成するときに、カスタムイベントargを作成するには:

  • EventArgsから派生したクラスを作成し、必要なデータのプロパティを定義します。
  • 規約として、クラスの名前はEventArgs終わる必要があります。

以下の例では、クラスのPriceプロパティのPriceChangingEventArgsイベントを作成します。イベントデータクラスには、 CurrentPriceNewPriceが含まれています。このイベントは、新しい値をPriceプロパティに割り当てると消費者に値が変化していることを知らせ、現在の価格と新しい価格について知らせるときに発生します。

PriceChangingEventArgs

public class PriceChangingEventArgs : EventArgs
{
    public PriceChangingEventArgs(int currentPrice, int newPrice)
    {
        this.CurrentPrice = currentPrice;
        this.NewPrice = newPrice;
    }

    public int CurrentPrice { get; private set; }
    public int NewPrice { get; private set; }
}

製品

public class Product
{
    public event EventHandler<PriceChangingEventArgs> PriceChanging;

    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(price, value);
            OnPriceChanging(e);
            price = value;
        }
    }

    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PriceChanging;
        if (handler != null)
            handler(this, e);
    }
}

消費者が新しい値を変更できるようにして、この例を拡張すると、その値がプロパティに使用されます。これを行うには、これらの変更をクラスに適用すれば十分です。

NewPriceの定義を変更可能に変更します。

public int NewPrice { get; set; }

e.NewPriceを呼び出した後、 OnPriceChangingをプロパティの値として使用するためにPriceの定義を変更します。

int price;
public int Price
{
    get { return price; }
    set
    {
        var e = new PriceChangingEventArgs(price, value);
        OnPriceChanging(e);
        price = e.NewPrice;
    }
}

キャンセル可能なイベントの作成

キャンセル可能なイベントは、クラスのFormClosingイベントなど、キャンセル可能なアクションを実行しようとしているときに発生させることができForm

このようなイベントを作成するには:

  • CancelEventArgsから派生した新しいイベント引数を作成し、イベントデータのプロパティを追加します。
  • EventHandler<T>を使用してイベントを作成し、作成した新しいキャンセルイベントargクラスを使用します。

以下の例では、クラスのPriceプロパティのPriceChangingEventArgsイベントを作成します。イベントデータクラスには、消費者が新しいものについて知ることができるValueが含まれています。このイベントは、新しい値をPriceプロパティに割り当てると消費者に値が変更されていることを通知し、イベントをキャンセルさせるときに発生します。消費者がイベントをキャンセルした場合、以前のPrice値が使用されます。

PriceChangingEventArgs

public class PriceChangingEventArgs : CancelEventArgs
{
    int value;
    public int Value
    {
        get { return value; }
    }
    public PriceChangingEventArgs(int value)
    {
        this.value = value;
    }
}

製品

public class Product
{
    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(value);
            OnPriceChanging(e);
            if (!e.Cancel)
                price = value;
        }
    }

    public event EventHandler<PriceChangingEventArgs> PropertyChanging;
    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PropertyChanging;
        if (handler != null)
            PropertyChanging(this, e);
    }
}

イベントのプロパティ

クラスがイベントの数を大きくした場合、デリゲートごとに1つのフィールドのストレージコストは受け入れられない可能性があります。 .NET Frameworkは、これらのケースのイベントプロパティを提供します。この方法で、 EventHandlerListなどの別のデータ構造を使用して、イベントデリゲートを格納できます。

public class SampleClass 
{
    // Define the delegate collection.
    protected EventHandlerList eventDelegates = new EventHandlerList();

    // Define a unique key for each event.
    static readonly object someEventKey = new object();

    // Define the SomeEvent event property.
    public event EventHandler SomeEvent
    {
        add
        {
            // Add the input delegate to the collection.
            eventDelegates.AddHandler(someEventKey, value);
        }
        remove
        {
            // Remove the input delegate from the collection.
            eventDelegates.RemoveHandler(someEventKey, value);
        }
    }

    // Raise the event with the delegate specified by someEventKey
    protected void OnSomeEvent(EventArgs e)
    {
        var handler = (EventHandler)eventDelegates[someEventKey];
        if (handler != null)
            handler(this, e);
    }
}

このアプローチは、WinFormsなどのGUIフレームワークで広く使用されています。このフレームワークでは、コントロールには数十件のイベントが含まれます。

EventHandlerListはスレッドセーフではないので、クラスを複数のスレッドから使​​用することを期待する場合は、ロックステートメントやその他の同期メカニズムを追加する必要があります(またはスレッドの安全性を提供するストレージを使用する必要があります)。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow