次の方法で共有


イベントを処理して発生させる

.NET のイベントは、デリゲート モデルに基づいています。 デリゲート モデルはオブ ザーバー 設計パターンに従います。これにより、サブスクライバーはプロバイダーに登録してプロバイダーからの通知を受信できます。 イベントの送信者は、イベントが発生したときに通知をプッシュします。 イベント レシーバーは応答を定義します。 この記事では、デリゲート モデルの主要なコンポーネント、アプリケーションでイベントを使用する方法、およびコードにイベントを実装する方法について説明します。

イベント送信者を使用してイベントを発生させる

イベントは、アクションの発生を通知するためにオブジェクトによって送信されるメッセージです。 アクションは、ボタンを押すなどのユーザー操作であるか、プロパティ値の変更など、他のプログラム ロジックによって発生する可能性があります。 イベントを発生させるオブジェクトは、 イベント送信者と呼ばれます。 イベント送信者は、発生したイベントを受信 (処理) するオブジェクトまたはメソッドを認識しません。 通常、イベントはイベント送信元のメンバーです。 たとえば、 Click イベントは Button クラスのメンバーであり、 PropertyChanged イベントは、 INotifyPropertyChanged インターフェイスを実装するクラスのメンバーです。

イベントを定義するには、イベント クラスのシグネチャで C# イベント または Visual Basic Event キーワードを使用し、イベントのデリゲートの種類を指定します。 デリゲートについては、次のセクションで説明します。

通常、イベントを発生させるために、(C# では) protectedvirtual 、または (Visual Basic では) ProtectedOverridable としてマークされたメソッドを追加します。 メソッドの名前付け規則としては、例としてOn<EventName>OnDataReceivedを使います。 メソッドは、イベント データ オブジェクト ( EventArgs 型または派生型のオブジェクト) を指定する 1 つのパラメーターを受け取る必要があります。 このメソッドを指定して、派生クラスがイベントを発生させるためのロジックをオーバーライドできるようにします。 派生クラスは、常に基底クラスの On<EventName> メソッドを呼び出して、登録されたデリゲートがイベントを受け取れるようにする必要があります。

次の例は、 ThresholdReachedという名前のイベントを宣言する方法を示しています。 イベントは EventHandler デリゲートに関連付けられ、 OnThresholdReachedという名前のメソッドで発生します。

class Counter
{
    public event EventHandler ThresholdReached;

    protected virtual void OnThresholdReached(EventArgs e)
    {
        ThresholdReached?.Invoke(this, e);
    }

    // provide remaining implementation for the class
}
Public Class Counter
    Public Event ThresholdReached As EventHandler

    Protected Overridable Sub OnThresholdReached(e As EventArgs)
        RaiseEvent ThresholdReached(Me, e)
    End Sub

    ' provide remaining implementation for the class
End Class

イベント ハンドラーのデリゲートシグネチャを宣言する

デリゲートは、メソッドへの参照を保持する型です。 デリゲートは、参照するメソッドの戻り値の型とパラメーターを示すシグネチャで宣言されます。 シグネチャに一致するメソッドへの参照のみを保持できます。 デリゲートは、型セーフな関数ポインターまたはコールバックと同等です。 デリゲートクラスを定義するには、デリゲート宣言で十分です。

.NET では、デリゲートに多くの用途があります。 イベントのコンテキストでは、デリゲートは、イベント ソースとイベントを処理するコードの間の中間 (またはポインターのようなメカニズム) です。 前のセクションの例に示されているように、デリゲート型をイベント宣言に含めることで、デリゲートをイベントに関連付けます。 デリゲートの詳細については、 Delegate クラスを参照してください。

.NET には、ほとんどのイベント シナリオをサポートするための EventHandler および EventHandler<TEventArgs> デリゲートが用意されています。 イベント データを含まないすべてのイベントに対して、 EventHandler デリゲートを使用します。 イベントに関するデータを含むイベントには、 EventHandler<TEventArgs> デリゲートを使用します。 これらのデリゲートには戻り値がなく、2 つのパラメーター (イベントのソースのオブジェクトとイベント データのオブジェクト) を受け取ります。

デリゲートは マルチキャスト クラス オブジェクトです。つまり、複数のイベント処理メソッドへの参照を保持できます。 詳細については、 Delegate リファレンス ページを参照してください。 デリゲートは、イベント処理の柔軟性ときめ細かな制御を提供します。 デリゲートは、イベントの登録済みイベント ハンドラーの一覧を維持することによってイベントを発生させるクラスのイベント ディスパッチャーとして機能します。

EventHandlerおよびEventHandler<TEventArgs>デリゲート型を使用して、必要なデリゲートを定義します。 デリゲートは、delegate では型、宣言では Delegate では型でマークします。 次の例は、 ThresholdReachedEventHandlerという名前のデリゲートを宣言する方法を示しています。

public delegate void ThresholdReachedEventHandler(object sender, ThresholdReachedEventArgs e);
Public Delegate Sub ThresholdReachedEventHandler(sender As Object, e As ThresholdReachedEventArgs)

イベント データ クラスの操作

イベントに関連付けられたデータは、イベント データ クラスを介して提供できます。 .NET には、アプリケーションで使用できる多くのイベント データ クラスが用意されています。 たとえば、 SerialDataReceivedEventArgs クラスは、 SerialPort.DataReceived イベントのイベント データ クラスです。 .NET は、すべてのイベント データ クラスが末尾に EventArgs サフィックスを付ける名前付けパターンに従います。 イベントのデリゲートを調べることで、イベントに関連付けられているイベント データ クラスを決定します。 たとえば、 SerialDataReceivedEventHandler デリゲートには、パラメーターとして SerialDataReceivedEventArgs クラスが含まれています。

EventArgs クラスは、通常、イベント データ クラスの基本型です。 イベントにデータが関連付けられていない場合も、このクラスを使用します。 追加のデータなしで何かが発生したことをサブスクライバーに通知するイベントを作成する場合は、デリゲートの 2 番目のパラメーターとして EventArgs クラスを含めます。 データが指定されていない場合は、 EventArgs.Empty 値を渡すことができます。 EventHandler デリゲートには、パラメーターとして EventArgs クラスが含まれています。

イベントに関連するデータを渡すために必要なメンバーを提供するために、 EventArgs クラスから派生するクラスを作成できます。 通常、.NET と同じ名前付けパターンを使用し、イベント データ クラス名を EventArgs サフィックスで終了する必要があります。

次の例は、発生するイベントに固有のプロパティを含む ThresholdReachedEventArgs という名前のイベント データ クラスを示しています。

public class ThresholdReachedEventArgs : EventArgs
{
    public int Threshold { get; set; }
    public DateTime TimeReached { get; set; }
}
Public Class ThresholdReachedEventArgs
    Inherits EventArgs

    Public Property Threshold As Integer
    Public Property TimeReached As DateTime
End Class

ハンドラーを使用してイベントに応答する

イベントに応答するには、イベント レシーバーでイベント ハンドラー メソッドを定義します。 このメソッドは、処理するイベントのデリゲートのシグネチャと一致する必要があります。 イベント ハンドラーでは、ユーザーがボタンを押した後にユーザー入力を収集するなど、イベントが発生したときに必要なアクションを実行します。 イベントが発生したときに通知を受信するには、イベント ハンドラー メソッドがイベントをサブスクライブする必要があります。

次の例は、c_ThresholdReached デリゲートのシグネチャと一致する EventHandler という名前のイベント ハンドラー メソッドを示しています。 メソッドは、 ThresholdReached イベントをサブスクライブします。

class ProgramTwo
{
    static void Main()
    {
        var c = new Counter();
        c.ThresholdReached += c_ThresholdReached;

        // provide remaining implementation for the class
    }

    static void c_ThresholdReached(object sender, EventArgs e)
    {
        Console.WriteLine("The threshold was reached.");
    }
}
Module Module1

    Sub Main()
        Dim c As New Counter()
        AddHandler c.ThresholdReached, AddressOf c_ThresholdReached

        ' provide remaining implementation for the class
    End Sub

    Sub c_ThresholdReached(sender As Object, e As EventArgs)
        Console.WriteLine("The threshold was reached.")
    End Sub
End Module

静的イベント ハンドラーと動的イベント ハンドラーを使用する

.NET を使用すると、サブスクライバーは静的または動的にイベント通知に登録できます。 静的イベント ハンドラーは、処理するイベントを持つクラスの有効期間全体で有効です。 動的イベント ハンドラーは、プログラムの実行中に明示的にアクティブ化および非アクティブ化されます。通常は、一部の条件付きプログラム ロジックに応答します。 動的ハンドラーは、特定の条件下でのみイベント通知が必要な場合や、ランタイム条件によって呼び出す特定のハンドラーが決定される場合に使用できます。 前のセクションの例では、イベント ハンドラーを動的に追加する方法を示します。 詳細については、「 イベント (Visual Basic の場合)」および 「イベント (C#)」を参照してください。

複数のイベントを発生させる

クラスが複数のイベントを発生させる場合、コンパイラはイベント デリゲート インスタンスごとに 1 つのフィールドを生成します。 イベントの数が多い場合、デリゲートごとに 1 つのフィールドのストレージ コストが許容されない可能性があります。 これらのシナリオでは、.NET には、イベント デリゲートを格納するために選択した別のデータ構造で使用できるイベント プロパティが用意されています。

イベント プロパティは、イベント アクセサーを伴うイベント宣言で構成されます。 イベント アクセサーは、ストレージ データ構造からイベント デリゲート インスタンスを追加または削除するために定義するメソッドです。

イベント のプロパティは、各イベント デリゲートを呼び出す前に取得する必要があるため、イベント フィールドよりも低速です。

トレードオフは、メモリと速度の間です。 発生頻度の低い多数のイベントがクラスで定義されている場合は、イベント プロパティを実装する必要があります。 詳細については、「 イベント プロパティを使用して複数のイベントを処理する」を参照してください。

次のリソースでは、イベントの操作に関連するその他のタスクと概念について説明します。

仕様リファレンスを確認する

仕様リファレンス ドキュメントは、イベント処理をサポートする API で使用できます。

API 名 [API の種類] リファレンス
EventHandler 委任 EventHandler
EventHandler<TEventArgs> 委任 EventHandler<TEventArgs>
イベント引数 クラス EventArgs
委任 クラス Delegate