イベント(event)とデリゲート(delegate)を自作しよう!

[C#] イベント(event)とデリゲート(delegate)を自作しよう!

※ 当サイトは広告を含みます。

ここではC#のイベントを自作する方法を学習します。
正直、定義済みを使うことのほうが多いのですが、知識的には理解すべきと思います。

なお、イベントの基礎知識は前回の記事で習得済みを前提に進めます。
基礎的な部分の解説は一切しないので、分からない人は先にこちらの記事を読んでください。

.NETのガイドライン

.NETにはイベント作成に関するガイドラインが存在します。
これに従っておくと、見た目が同じで誰にでも分かりやすいイベントを作成できます。


public delegate void EventHandler(object? sender, EventArgs e);

公式ガイドラインhttps://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-publish-events-that-conform-to-net-framework-guidelines

自作のイベントを作成

最初に完成版を見せます。これは一定時間(1秒)ごとに通知を発生させるプログラムです。
通知の部分がイベントになっていて、メイン処理に何も書かなくとも画面には現在時刻が表示されます。


namespace Sample
{
  internal class Program
  {
    // 自作のEventArgs
    public class TimeEventArgs : EventArgs
    {
      public TimeEventArgs(DateTime now)
      {
        this.Now = now;
      }

      public DateTime Now { get; private set; }
    }

    // 自作のデリゲート (イベントハンドラ)
    public delegate void TimeEventHandler(object? sender, TimeEventArgs e);

    // 自作のイベント発行元
    class Worker
    {
      // イベント発生時の処理登録用
      public event TimeEventHandler Notice;

      // 発行元の名前
      public string Name { get; set; }

      public Worker(string name)
      {
        this.Name = name;
      }

      // 一定時間(1秒)ごとに通知を発生させるメソッド
      public void Run()
      {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        while (true)
        {
          if (sw.Elapsed > TimeSpan.FromSeconds(1))
          {
            // 自作イベントを発生させる
            if (this.Notice != null)
              this.Notice(this, new TimeEventArgs(DateTime.Now));

            sw.Restart();
          }
        }
      }
    }

    static void Main(string[] args)
    {
      var worker = new Worker("観測者");

      // イベントハンドラに処理を登録
      worker.Notice += Worker_OnNotice;

      worker.Run();
    }

    // イベント発生時の処理
    private static void Worker_OnNotice(object? sender, TimeEventArgs e)
    {
      // 発行元にキャストして利用する
      if (sender is Worker w)
        Console.WriteLine($"{w?.Name}: {e?.Now.ToString()}");
    }
  }
}

注意このプログラムを止める方法はありません。Visual Studioの機能で実行して下さい。

EventArgsを作成

最初に通知発生時に利用するパラメータ用のクラスを作成します。
そして、イベント発生時の時刻を記録するプロパティを所有させます。


// 自作のEventArgs
public class TimeEventArgs : EventArgs
{
  public TimeEventArgs(DateTime now)
  {
    this.Now = now;
  }

  public DateTime Now { get; private set; }
}

デリゲートを作成

次にデリゲートを定義します。パラメータには作成したクラス(TimeEventArgs型)を指定します。


// 自作のデリゲート (イベントハンドラ)
public delegate void TimeEventHandler(object? sender, TimeEventArgs e);

イベント発行元を作成

次は作成したデリゲートをイベント型として所有し、一定時間ごとに通知(イベント)を発生させるクラスを作成します。


// 自作のイベント発行元
class Worker
{
  // イベント発生時の処理登録用
  public event TimeEventHandler Notice;

  // 発行元の名前
  public string Name { get; set; }

  public Worker(string name)
  {
    this.Name = name;
  }

  // 一定時間(1秒)ごとに通知を発生させるメソッド
  public void Run()
  {
    var sw = System.Diagnostics.Stopwatch.StartNew();
    while (true)
    {
      if (sw.Elapsed > TimeSpan.FromSeconds(1))
      {
        // 自作イベントを発生させる
        if (this.Notice != null)
          this.Notice(this, new TimeEventArgs(DateTime.Now));

        sw.Restart();
      }
    }
  }
}

まず、イベントはこう書きます。簡易記述になっていてadd/removeを書く必要はありません。


// イベント発生時の処理登録用
public event TimeEventHandler Notice;

そしてイベントを発生させる部分はここです。
イベントは未登録の場合がありえるので、普通はnullチェックします。


// 自作イベントを発生させる
if (this.Notice != null)
  this.Notice(this, new TimeEventArgs(DateTime.Now));
管理人
管理人

その他の処理は今回の仕様を満たすために必要な記述です。

Main関数を作成

最後にMain関数でインスタンスの作成とイベントハンドラへのメソッド登録を行います。


static void Main(string[] args)
{
  var worker = new Worker("観測者");

  // イベントハンドラに処理を登録
  worker.Notice += Worker_OnNotice;

  worker.Run();
}

// イベント発生時の処理
private static void Worker_OnNotice(object? sender, TimeEventArgs e)
{
  // 発行元にキャストして利用する
  if (sender is Worker w)
    Console.WriteLine($"{w?.Name}: {e?.Now.ToString()}");
}

全体の再確認

これらを全て記述したのが最初のコードです。改めて載せておきます。


namespace Sample
{
  internal class Program
  {
    // 自作のEventArgs
    public class TimeEventArgs : EventArgs
    {
      public TimeEventArgs(DateTime now)
      {
        this.Now = now;
      }

      public DateTime Now { get; private set; }
    }

    // 自作のデリゲート (イベントハンドラ)
    public delegate void TimeEventHandler(object? sender, TimeEventArgs e);

    // 自作のイベント発行元
    class Worker
    {
      // イベント発生時の処理登録用
      public event TimeEventHandler Notice;

      // 発行元の名前
      public string Name { get; set; }

      public Worker(string name)
      {
        this.Name = name;
      }

      // 一定時間(1秒)ごとに通知を発生させるメソッド
      public void Run()
      {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        while (true)
        {
          if (sw.Elapsed > TimeSpan.FromSeconds(1))
          {
            // 自作イベントを発生させる
            if (this.Notice != null)
              this.Notice(this, new TimeEventArgs(DateTime.Now));

            sw.Restart();
          }
        }
      }
    }

    static void Main(string[] args)
    {
      var worker = new Worker("観測者");

      // イベントハンドラに処理を登録
      worker.Notice += Worker_OnNotice;

      worker.Run();
    }

    // イベント発生時の処理
    private static void Worker_OnNotice(object? sender, TimeEventArgs e)
    {
      // 発行元にキャストして利用する
      if (sender is Worker w)
        Console.WriteLine($"{w?.Name}: {e?.Now.ToString()}");
    }
  }
}

あとがき

イベントを本格的に利用するのはGUI系のプログラムです。CLIでの利用は稀。
ただ、GUI系でもイベントを使うことはあっても作ることは殆ど無いと思います。

◆ C#に関する学習コンテンツ

この記事は参考になりましたか?

関連記事

コメント

この記事へのコメントはありません。