English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

C# イベント(Event)

C# 面向オブジェクト(OOP)

イベントはオブジェクトが操作の発生を示すために送信する通知です。.NETのイベントはオブザーバーデザインパターンに従います。 イベントを発生させるクラスは

パブリッシャー(发布者)、通知を受け取るクラスはサブスクライバー(订阅者)と呼ばれます。イベントには複数のサブスクライバーが存在できます。通常、パブリッシャーはある操作が発生したときにイベントを発生させます。サブスクライバーは操作が発生したときに通知を受け取りたい場合は、イベントに登録し、それを処理する必要があります。

C#では、イベントはデリゲートに依存しています。デリゲートは、サブスクライバークラスのイベントハンドラメソッドのシグネチャを定義します。

以下はC#のイベントを説明する図です。

イベントパブリッシャーやサブスクライバー

イベントを使用してデリゲートを通じて パブリッシャー(publisher) クラス。他のイベントを受け取るクラスは、サブスクライバー(subscriber)クラスと呼ばれます。イベントは、同じクラスや他のクラスのデリゲートとイベントハンドラを関連付けるために使用されます。イベントを含むクラスは、イベントを発行するために使用されます。これは サブスクライバー-サブスクライバー(publisher-サブスクライバー(subscriber) モデル。

パブリッシャー(publisher)- イベントとデリゲートの定義を含むオブジェクトです。イベントとデリゲートの関係もこのオブジェクトで定義されています。パブリッシャークラスのオブジェクトがこのイベントを呼び出し、他のオブジェクトに通知します。

購読者- イベントを提供し、イベントハンドラを提供するオブジェクトです。出版者クラスのデリゲートは、購読者クラスのイベントハンドラを呼び出します。

イベントの宣言

イベントを宣言するには二つのステップが必要です:

  1. デリゲートの宣言

  2. デリゲートの変数を使用して event キーワード

以下の例では、出版者クラスでイベントを宣言する方法を示します。

public delegate void Notify();  // デリゲート
                    
public class ProcessBusinessLogic
{
    public event Notify ProcessCompleted; // イベント
}

上記の例では、Notify デリゲートを宣言し、ProcessBusinessLogic クラスで 'event' キーワードを使用して Notify デリゲートの型を持つイベント ProcessCompleted を宣言しました。したがって、ProcessBusinessLogic クラスは publisher(出版者)と呼ばれます。Notify デリゲートは ProcessCompleted イベントハンドラーメソッドのシグネチャを指定します。これにより、subscriber(購読者)クラスのイベントハンドラーメソッドは void 返り値と引数を持つ必要があります。

今度は、ProcessCompleted イベントを発生させる方法を見てみましょう。以下の実装をご覧ください。

public delegate void Notify();  // デリゲート
                    
public class ProcessBusinessLogic
{
    public event Notify ProcessCompleted; // イベント
    public void StartProcess()
    {
        Console.WriteLine("Process Started!");
        // ここにいくつかのコードがあります...
        OnProcessCompleted();
    }
    protected virtual void OnProcessCompleted() //protected virtual メソッド
    {
        //ProcessCompleted が null でない場合にデリゲートを呼び出します。
        ProcessCompleted?.Invoke(); 
    }
}

上記の通り、StartProcess() メソッドの最後に onProcessCompleted() メソッドが呼び出されます。これによりイベントが発生します。通常、イベントを発生させるためには、<EventName> 上の名前で protected と virtual メソッドを定義します。protected と virtual は、派生クラスがイベントを発生させるロジックをオーバーライドするために使用されます。ただし、派生クラスは常に基底クラスの On<EventName> メソッドを呼び出す必要があります。これにより、登録されたデリゲートがイベントを受け取ります。

OnProcessCompleted() メソッドは ProcessCompleted?. invoke() でデリゲートを呼び出します。これにより、ProcessCompleted に登録されたすべてのイベントハンドラーメソッドが呼び出されます。

サブスクライバークラスは、以下のようにProcessCompletedイベントに登録され、Notifyデリゲートのシグネチャに一致するメソッドを使用して処理する必要があります。

class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // イベントを登録する
        bl.StartProcess();
    }
    // イベントハンドラ
    public static void bl_ProcessCompleted()
    {
        Console.WriteLine("プロセス完了!");
    }
}

上記では、ProgramクラスがProcessCompleted イベントのサブスクライバーは、以下のように使用します + =演算子でイベントに登録します。これと、マルチキャストデリゲートの呼び出しリストにメソッドを追加する方法は同じです。bl_processcompleted()メソッドがイベントを処理するのは、Notifyデリゲートのシグネチャに一致しているからです。

内蔵EventHandlerデリゲート

.NET Frameworkには、最も一般的なイベント用に内蔵されたデリゲートタイプであるEventHandlerおよびEventHandler<TEventArgs>が含まれています。通常、どんなイベントも二つのパラメータを含むべきです:イベントソースとイベントデータ。イベントデータを含まないすべてのイベントにはEventHandlerデリゲートを使用します。データを処理プログラムに送信する必要があるイベントには、EventHandler<TEventArgs>デリゲートを使用します。

上記の例では、カスタムNotifyデリゲートを宣言することなく、EventHandlerデリゲートを使用することができます。

class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // イベント登録
        bl.StartProcess();
    }
    // イベントハンドラ
    public static void bl_ProcessCompleted(object sender, EventArgs e)
    {
        Console.WriteLine("プロセス完了!");
    }
}
public class ProcessBusinessLogic
{
    // 内蔵のEventHandlerを使用してイベントを宣言します。
    public event EventHandler ProcessCompleted; 
    public void StartProcess()
    {
        Console.WriteLine("Process Started!");
        // ここにいくつかのコードがあります...
        OnProcessCompleted(EventArgs.Empty); //イベントデータなし
    }
    protected virtual void OnProcessCompleted(EventArgs e)
    {
        ProcessCompleted?.Invoke(this, e);
    }
}

上記の例では、イベントハンドラbl_ProcessCompleted()メソッドには、EventHandlerデリゲートに一致する二つのパラメータが含まれています。同時に、thisを送信者、EventArgsをイベントデータとして渡します。OnProcessCompleted()メソッドでInvoke()を呼び出してイベントを発生させるときは、引数は空です。なぜなら、イベントにはデータが不要で、プロセスが完了したことをサブスクライバーに通知するだけだからです。EventArgs.Emptyを渡しています。

イベントデータの渡し

ほとんどのイベントはデータをサブスクライバーに送信します。EventArgsクラスはすべてのイベントデータクラスの基底クラスです。.NETには、SerialDataReceivedEventArgsなどの多くのビルドインイベントデータクラスがあります。イベントデータクラスの名前は EventArgs で終わるように命名されています。EventArgsクラスを派生して、カスタムイベントデータクラスを作成することができます。

以下のように EventHandler <TEventArgs> を使用してデータをハンドラに渡します。

class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // イベント登録
        bl.StartProcess();
    }
    // イベントハンドラ
    public static void bl_ProcessCompleted(object sender, bool IsSuccessful)
    {
        Console.WriteLine("Process " + (IsSuccessful? "Completed Successfully": "failed"));
    }
}
public class ProcessBusinessLogic
{
    // 内蔵のEventHandlerを使用してイベントを宣言します。
    public event EventHandler<bool> ProcessCompleted; 
    public void StartProcess()
    {
        try
        {
            Console.WriteLine("Process Started!");
            // ここにいくつかのコードがあります...
            OnProcessCompleted(true);
        }
        catch(Exception ex)
        {
            OnProcessCompleted(false);
        }
    }
    protected virtual void OnProcessCompleted(bool IsSuccessful)
    {
        ProcessCompleted?.Invoke(this, IsSuccessful);
    }
}

上記の例では、プロセスが成功したかどうかを示す単一のボルン値をハンドラに渡します。

複数の値をイベントデータとして渡したい場合、EventArgsの基底クラスから派生したクラスを作成することができます。

class ProcessEventArgs : EventArgs
{
    public bool IsSuccessful { get; set; }
    public DateTime CompletionTime { get; set; }
}

以下の例では、カスタム ProcessEventArgs クラスをハンドラに渡す方法を示します。

class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // イベント登録
        bl.StartProcess();
    }
    // イベントハンドラ
    public static void bl_ProcessCompleted(object sender, ProcessEventArgs e)
    {
        Console.WriteLine("Process " + (e.IsSuccessful ? "Completed Successfully" : "failed"));
        Console.WriteLine("Completion Time: " + e.CompletionTime.ToLongDateString());
    }
}
public class ProcessBusinessLogic
{
    // 内蔵のEventHandlerを使用してイベントを宣言します。
    public event EventHandler<ProcessEventArgs> ProcessCompleted; 
    public void StartProcess()
    {
        var data = new ProcessEventArgs();
        try
        {
            Console.WriteLine("Process Started!");
            // ここにいくつかのコードがあります...
            
            data.IsSuccessful = true;
            data.CompletionTime = DateTime.Now;
            OnProcessCompleted(data);
        }
        catch(Exception ex)
        {
            data.IsSuccessful = false;
            data.CompletionTime = DateTime.Now;
            OnProcessCompleted(data);
        }
    }
    protected virtual void OnProcessCompleted(ProcessEventArgs e)
    {
        ProcessCompleted?.Invoke(this, e);
    }
}

したがって、C#でイベントを作成、発生させ、登録、処理することができます。

覚えておくべきポイント

  1. イベントはデリゲートを包装しています。これはデリゲートに依存しています。

  2. イベントを宣言するために、"event"キーワードとデリゲートタイプの変数を一緒に使用します。

  3. 内蔵のデリゲートを使用します。EventHandler またはEventHandler <TEventArgs> は一般的なイベントに使用されます。

  4. リリースャブがイベントを発生させ、サブスクライバャブがイベントを登録し、イベントハンドラメソッドを提供します。

  5. イベントを発生させるメソッドはイベント名で名付けられ、'On'で前置されます。

  6. ハンドラメソッドのシグネチャはデリゲートのシグネチャと一致する必要があります。

  7. 使用+ =演算子でイベントを登録します。使用 -=演算子で解除登録を行うことができません。=演算子を使用できません。

  8. EventHandler <TEventArgs>を使用してイベントデータを渡します。

  9. カスタムイベントデータクラスを作成するために EventArgs 基クラスを派生させます。

  10. イベントは静的、仮想、密封、抽象(static, virtual, sealed, abstract)として宣言できます。

  11. インターフェースはイベントをメンバーとして含むことができます。

  12. 複数のサブスクライバーがある場合、イベントハンドラを同期で呼び出します。