C# イベント(Event)

C# のイベント(Event)は、特定のイベント通知を購読者に送信するためのメンバーです。イベントは通常、オブザーバーパターンを実装するために使用され、あるオブジェクトが他のオブジェクトの詳細を知らなくても、状態の変化を他のオブジェクトに通知できるようにします。

イベント(Event)基本的には、キー入力、クリック、マウス移動などのユーザー操作、またはシステム生成の通知などの通知情報です。アプリケーションはイベントが発生したときにイベントに応答する必要があります。例えば、割り込みです。

C# ではイベントメカニズムを使用してスレッド間通信を実現します。

重要なポイント:

  • デリゲートの宣言:イベントが使用するデリゲート型を定義します。デリゲートは関数シグネチャです。
  • イベントの宣言:使用するeventキーワードでイベントを宣言します。
  • イベントのトリガー:適切なタイミングでイベントを呼び出し、すべての購読者に通知します。
  • イベントの購読と購読解除:他のクラスは+=と-=演算子を使用してイベントを購読および購読解除できます。

イベントを介してデリゲートを使用する

イベントはクラス内で宣言および生成され、同じクラスまたは他のクラス内のデリゲートを使用してイベントハンドラーと関連付けられます。イベントを含むクラスはイベントを公開(パブリッシュ)するために使用されます。これはパブリッシャー(publisher)クラスと呼ばれます。このイベントを受け取る他のクラスはサブスクライバー(subscriber)クラスと呼ばれます。イベントはパブリッシュ-サブスクライブ(publisher-subscriber)モデルを使用します。

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

サブスクライバー(subscriber)は、イベントを受け取り、イベントハンドラーを提供するオブジェクトです。パブリッシャー(publisher)クラス内のデリゲートが、サブスクライバー(subscriber)クラス内のメソッド(イベントハンドラー)を呼び出します。

イベント(Event)の宣言

クラス内でイベントを宣言するには、まずそのイベントのデリゲート型を宣言する必要があります。例:

public delegate void BoilerLogHandler(string status);

次に、イベント自体を宣言します。使用するeventキーワード:

// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;

上記のコードは、BoilerLogHandlerという名前のデリゲートと、BoilerEventLogという名前のイベントを定義しています。このイベントは生成時にデリゲートを呼び出します。

次の例は、C# でイベントを使用する方法を示しています:

例

using System;

namespace EventDemo
{
    // イベントハンドラー用のデリゲート型を定義する
    public delegate void NotifyEventHandler(object sender, EventArgs e);

    // パブリッシャークラス
    public class ProcessBusinessLogic
    {
        // イベントを宣言する
        public event NotifyEventHandler ProcessCompleted;

        // イベントをトリガーするメソッド
        protected virtual void OnProcessCompleted(EventArgs e)
        {
            ProcessCompleted?.Invoke(this, e);
        }

        // ビジネスロジックのプロセスをシミュレートし、イベントをトリガーする
        public void StartProcess()
        {
            Console.WriteLine("Process Started!");

            // ここに実際のビジネスロジックを追加できます

            // ビジネスロジックが完了し、イベントをトリガーする
            OnProcessCompleted(EventArgs.Empty);
        }
    }

    // サブスクライバークラス
    public class EventSubscriber
    {
        public void Subscribe(ProcessBusinessLogic process)
        {
            process.ProcessCompleted += Process_ProcessCompleted;
        }

        private void Process_ProcessCompleted(object sender, EventArgs e)
        {
            Console.WriteLine("Process Completed!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ProcessBusinessLogic process = new ProcessBusinessLogic();
            EventSubscriber subscriber = new EventSubscriber();

            // イベントを購読する
            subscriber.Subscribe(process);

            // プロセスを開始する
            process.StartProcess();

            Console.ReadLine();
        }
    }
}

説明

1. デリゲート型を定義する:

public delegate void NotifyEventHandler(object sender, EventArgs e);

これは、イベントハンドラーのシグネチャを定義するデリゲート型です。通常はEventHandler 或 EventHandler<TEventArgs>を使用してカスタムデリゲートの代わりにします。

2. イベントを宣言する:

public event NotifyEventHandler ProcessCompleted;

これは NotifyEventHandler デリゲート型を使用するイベントです。

3. イベントをトリガーする:

protected virtual void OnProcessCompleted(EventArgs e)
{
    ProcessCompleted?.Invoke(this, e);
}

これはイベントをトリガーするためのプロテクトメソッドです。使用する?.Invoke構文を使用して、サブスクライバーがいる場合にのみイベントを呼び出します。

4. イベントの購読と購読解除:

process.ProcessCompleted += Process_ProcessCompleted;

サブスクライバーは+=演算子を使用してイベントを購読し、イベントハンドラーを定義します。Process_ProcessCompleted。

例

例 1

using System;
namespace SimpleEvent
{
  using System;
  /***********パブリッシャークラス***********/
  public class EventTest
  {
    private int value;

    public delegate void NumManipulationHandler();


    public event NumManipulationHandler ChangeNum;
    protected virtual void OnNumChanged()
    {
      if ( ChangeNum != null )
      {
        ChangeNum(); /* イベントがトリガーされる */
      }else {
        Console.WriteLine( "event not fire" );
        Console.ReadKey(); /* エンターで続行 */
      }
    }


    public EventTest()
    {
      int n = 5;
      SetValue( n );
    }


    public void SetValue( int n )
    {
      if ( value != n )
      {
        value = n;
        OnNumChanged();
      }
    }
  }


  /***********サブスクライバークラス***********/

  public class subscribEvent
  {
    public void printf()
    {
      Console.WriteLine( "event fire" );
      Console.ReadKey(); /* エンターで続行 */
    }
  }

  /***********トリガー***********/
  public class MainClass
  {
    public static void Main()
    {
      EventTest e = new EventTest(); /* オブジェクトをインスタンス化、最初はイベントをトリガーしない */
      subscribEvent v = new subscribEvent(); /* オブジェクトをインスタンス化 */
      e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 登録 */
      e.SetValue( 7 );
      e.SetValue( 11 );
    }
  }
}

上記のコードをコンパイルして実行すると、次の結果が生成されます:

event not fire
event fire
event fire

この例では、温水ボイラーシステムのトラブルシューティング用の簡単なアプリケーションを提供します。保守エンジニアがボイラーを検査すると、ボイラーの温度と圧力が保守エンジニアのメモとともにログファイルに自動的に記録されます。

例 2

using System;
using System.IO;

namespace BoilerEventAppl
{
    // Boiler クラス
    class Boiler
    {
        public int Temp { get; private set; }
        public int Pressure { get; private set; }

        public Boiler(int temp, int pressure)
        {
            Temp = temp;
            Pressure = pressure;
        }
    }

    // イベントパブリッシャー
    class DelegateBoilerEvent
    {
        public delegate void BoilerLogHandler(string status);

        // 上記のデリゲートに基づいてイベントを定義する
        public event BoilerLogHandler BoilerEventLog;

        public void LogProcess()
        {
            string remarks = "O.K.";
            Boiler boiler = new Boiler(100, 12);
            int temp = boiler.Temp;
            int pressure = boiler.Pressure;

            if (temp > 150 || temp < 80 || pressure < 12 || pressure > 15)
            {
                remarks = "Need Maintenance";
            }

            OnBoilerEventLog($"Logging Info:\nTemperature: {temp}\nPressure: {pressure}\nMessage: {remarks}");
        }

        protected void OnBoilerEventLog(string message)
        {
            BoilerEventLog?.Invoke(message);
        }
    }

    // このクラスはログファイルへの書き込み条項を保持します
    class BoilerInfoLogger : IDisposable
    {
        private readonly StreamWriter _streamWriter;

        public BoilerInfoLogger(string filename)
        {
            _streamWriter = new StreamWriter(new FileStream(filename, FileMode.Append, FileAccess.Write));
        }

        public void Logger(string info)
        {
            _streamWriter.WriteLine(info);
        }

        public void Dispose()
        {
            _streamWriter?.Close();
        }
    }

    // イベントサブスクライバー
    public class RecordBoilerInfo
    {
        static void Logger(string info)
        {
            Console.WriteLine(info);
        }

        static void Main(string[] args)
        {
            using (BoilerInfoLogger fileLogger = new BoilerInfoLogger("e:\\boiler.txt"))
            {
                DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent();
                boilerEvent.BoilerEventLog += Logger;
                boilerEvent.BoilerEventLog += fileLogger.Logger;
                boilerEvent.LogProcess();
            }

            Console.ReadLine();
        }
    }
}

上記のコードをコンパイルして実行すると、次の結果が生成されます:

Logging info:

Temperature 100
Pressure 12

Message: O. K
その他の拡張