C# における EventHandler と Action の使い分け

〜 WinForms の Click イベントを題材に 〜

はじめに

C# を学び始めると必ず出てくるのが イベント です。

Button.Click や Form.Load のような UI 操作に反応する仕組みは、すべてイベントとして提供されます。

そのとき必ず目にするのが、次のようなコードです。

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

一方で C# には Action という汎用デリゲート型も存在します。

「これって Action<object?, EventArgs> で置き換えられるのでは?」と思った方も多いのではないでしょうか。

今回は EventHandler と Action の違い、使い分けのポイント を整理します。


EventHandler とは

EventHandler は .NET が定義する イベント専用のデリゲート型 です。

public delegate void EventHandler(object? sender, EventArgs e);
  • sender: イベント発生元(例えばクリックされた Button)
  • e: イベントデータ。Click の場合は EventArgs.Empty が渡される

さらに汎用化した EventHandler<TEventArgs> も用意されており、独自のイベントデータを渡すことができます。

public event EventHandler<MyEventArgs>? SomethingHappened;

これらは UI フレームワークや Visual Studio のデザイナ機能に最適化されており、イベントの自動生成や接続がスムーズに行えます。


Action とは

Action は .NET 標準ライブラリで提供される 汎用デリゲート型 です。

  • Action …… 引数なし
  • Action<T1> …… 引数1つ
  • Action<T1, T2> …… 引数2つ
  • …最大16個まで

例えば Action<object?, EventArgs> は EventHandler とシグネチャが同じです。

Action<object?, EventArgs> callback = (sender, e) =>
{
    Console.WriteLine("Action で受け取りました");
};

つまり技術的には EventHandler の代わりに Action を使うことは可能 です。


ではなぜ EventHandler を使うのか?

1. 慣例と統一性

  • .NET のイベントは 「sender + EventArgs」 という統一的なパターンに基づいています。
  • これを守ることで、学習コストを減らし、API の使い勝手を揃えられます。

2. デザイナ連携

  • WinForms や WPF のデザイナは EventHandler を前提にしています。
  • Action に変えるとプロパティウィンドウからのハンドラ生成が効かなくなります。

3. 拡張性

  • Action だと「引数を増やしたい」となったときに破壊的変更になりがちです。
  • EventHandler<TEventArgs> なら EventArgs にプロパティを足すだけで拡張できます。

使い分けの指針

✅ EventHandler を使うべきケース

  • 公開 API(UI イベントやライブラリの公開イベント)
  • Visual Studio デザイナと連携させる必要がある場合
  • 将来イベントデータを拡張する可能性がある場合

✅ Action を使ってよいケース

  • クラス内部の簡易コールバック
  • sender や EventArgs が不要で、単に「処理完了を通知したい」だけの場合
  • 公開しない一時的な処理差し替え(戦略注入)として利用する場合

サンプルコードで比較

EventHandler を使った公開イベント

public class Game
{
    public event EventHandler<ScoreChangedEventArgs>? ScoreChanged;

    protected virtual void OnScoreChanged(int oldScore, int newScore)
        => ScoreChanged?.Invoke(this, new ScoreChangedEventArgs(oldScore, newScore));
}

Action を使った内部コールバック

public class Worker
{
    public Action? Completed { get; set; }

    public async Task RunAsync()
    {
        // …処理
        Completed?.Invoke();
    }
}

まとめ

  • Action<object?, EventArgs> は技術的に EventHandler と互換。だが イベントの世界では EventHandler が標準
  • WinForms/WPF/ASP.NET など UI 系や公開ライブラリでは 必ず EventHandler を使う。
  • Action は 内部専用の軽量な通知 に向いている。

「公開 API → EventHandler」「内部コールバック → Action」 という切り分けを覚えておくと設計がすっきりします。


訪問数 5 回, 今日の訪問数 5回