C#入門:Actionとイベントを図解で学ぼう

1. はじめに

プログラムを作っていると、「ボタンを押したら処理を実行する」 という動きが欲しくなることがあります。

C#では、これを Action と イベント を使って作ることができます。

この記事では、図解付きでわかりやすく Action とイベントを学んでいきます。


2. まずはシンプルに体験しよう

次のコードをコピーして実行してみましょう。

using System;

class Program
{
    class Button
    {
        public event Action? Clicked;

        public void Click()
        {
            Clicked?.Invoke();
        }
    }

    static void Main()
    {
        var button = new Button();

        button.Clicked += () => Console.WriteLine("ボタンが押されました!");
        button.Clicked += () => Console.WriteLine("もうひとつの処理も実行されました!");

        button.Click();
    }
}

コードの ? は、「この変数(またはイベント)は、値が入っていない(= null)かもしれない」 という意味です。


イベントと null

C# でイベントを作るとき、最初はだれも登録していないので null になります。

public event Action? Clicked;
  • Clicked にだれも登録していない → 中身は null
  • だれかが += で処理を登録すると → null ではなくなる

Action と Action? の違い

  • Action(?なし) … 「必ず何かしらの処理が入っているはず」
  • Action?(?あり) … 「処理が入っていない(null)の場合もある」

イベントは最初は「登録なし=null」なので、? をつけるのが正解です。


呼び出すときの安全確認

イベントを呼び出すときは、

Clicked?.Invoke();

と書きます。

これは「もし Clicked が null じゃなければ呼ぶ」という意味です。

(もし null なら何もしません)


まとめ

  • ? は「null でもいいよ」という印。
  • イベントは最初は null なので Action? とする。
  • 呼び出すときは ?.Invoke() で「null じゃなければ呼ぶ」と安全に書ける。

public event Action? Clicked; の event キーワードの有無 には、初心者が混乱しやすい違いがあります。


1. event キーワードあり

public event Action? Clicked;

これは「イベント専用の仕組み」を定義しています。

  • 外部からできること
    • += で処理を追加する
    • -= で処理を削除する
  • 外部からできないこと
    • Clicked = null; のように直接代入することはできない
    • Clicked(); のように直接呼び出すことはできない

つまり「イベントの管理者(このクラスの中のコード)だけが発火できる」「外からは購読(+= / -=)だけ可能」という、安全な使い方に限定されます。


2. event キーワードなし

public Action? Clicked;

こちらは 単なるデリゲート変数 です。

  • 外部からも Clicked = …; と直接上書きできてしまう
  • 登録していた処理が消えてしまう危険がある
  • 外部から Clicked(); を呼び出せてしまう

つまり「イベントっぽいけど、制約がないので事故が起きやすい」です。


3. なぜ event を使うのか?

イベントは「クラスの利用者(外部)が勝手に書き換えられない」ようにしておくのが基本です。

そのため、イベントの定義には必ず event を付けるのが推奨されます。


4. まとめ

  • event あり → イベントとして安全に使える(外部は購読だけできる)
  • event なし → 単なるデリゲートで、外部から直接代入や呼び出しが可能になってしまう

これは イベントに処理を登録する書き方 です。


1. 「イベントに処理を登録する」とは?

イベント (Clicked) は「ボタンが押されたときに呼ばれる場所」を用意しておくものです。

そこに「この処理をやって!」と登録するのが += です。


2. () => Console.WriteLine(“ボタンが押されました!"); の意味

これは 「ラムダ式」 という書き方です。

  • () は「引数がない」という意味
  • => は「~したら」という矢印
  • Console.WriteLine(“ボタンが押されました!"); は「実際に行う処理」

つまり

() => Console.WriteLine("ボタンが押されました!")

「引数なしで呼ばれたら、画面に文字を表示する処理」

をひとまとまりにしたものです。


3. button.Clicked += … の意味

  • button.Clicked は「ボタンが押されたときに実行されるイベント」
  • += は「処理を追加する」

したがって

button.Clicked += () => Console.WriteLine("ボタンが押されました!");

は 「ボタンが押されたとき、この処理も実行するように登録する」 という意味です。


4. 実行の流れ

  1. プログラム開始 → イベントにはまだ何も登録されていない
  2. += で「ボタンが押されたときの処理」を登録する
  3. button.Click(); が呼ばれると、イベントが発生して登録した処理が実行される

まとめ

  • += は「イベントに処理を追加する」
  • () => … は「呼ばれたときに実行する処理をひとまとめにしたもの」
  • イベントは「ボタンが押された!」を知らせ、登録した処理が順番に実行される

実行結果

ボタンが押されました!
もうひとつの処理も実行されました!

3. 図で見る「イベントの流れ」

ボタンがクリックされると、イベントに登録された処理が順番に呼び出されるイメージです。

イラストの場合

PlantUMLによるシーケンス図

図にするとこういう流れになります:

  • ユーザーがボタンをクリック
  • Button が Clicked?.Invoke() を実行
  • 登録された処理(処理1, 処理2)が順番に呼び出される

4. 実際の書き方(独立クラス)

サンプルではインナークラスにしていましたが、実際は Button を独立させるほうがわかりやすいです。

using System;

class Button
{
    public event Action? Clicked;

    public void Click()
    {
        Clicked?.Invoke();
    }
}

class Program
{
    static void Main()
    {
        var button = new Button();

        button.Clicked += () => Console.WriteLine("ボタンが押されました!");
        button.Clicked += () => Console.WriteLine("もうひとつの処理も実行されました!");

        button.Click();
    }
}

5. 練習問題

  1. Button に「Hovered」というイベントを追加して、「マウスがボタンに乗ったとき」にメッセージを表示してみよう。
  2. button.Clicked に3つ以上の処理を登録したら、どの順番で呼ばれるかを確かめてみよう。
  3. 次のような RunProcess メソッドを作って、Action を渡して実行してみよう。
static void RunProcess(Action action)
{
    Console.WriteLine("処理開始");
    action();
    Console.WriteLine("処理終了");
}

① Button に「Hovered」イベントを追加

EventHandler を使って独自のイベントを追加できます。

using System;

class Button
{
    public event Action Clicked;
    public event Action Hovered;

    public void OnClick() => Clicked?.Invoke();
    public void OnHover() => Hovered?.Invoke();
}

class Program
{
    static void Main()
    {
        var button = new Button();

        // イベント登録
        button.Hovered += () => Console.WriteLine("マウスがボタンに乗りました!");
        button.Clicked += () => Console.WriteLine("ボタンがクリックされました!");

        // 実行テスト
        button.OnHover();  // Hover イベント
        button.OnClick();  // Click イベント
    }
}

実行結果(例)

マウスがボタンに乗りました!
ボタンがクリックされました!

② Clicked に複数の処理を登録 → 順番を確認

イベントには複数のメソッドを追加できます。その場合、登録した順番に呼ばれます

using System;

class Button
{
    public event Action Clicked;
    public void OnClick() => Clicked?.Invoke();
}

class Program
{
    static void Main()
    {
        var button = new Button();

        button.Clicked += () => Console.WriteLine("処理1");
        button.Clicked += () => Console.WriteLine("処理2");
        button.Clicked += () => Console.WriteLine("処理3");

        button.OnClick();
    }
}

実行結果

処理1
処理2
処理3

③ RunProcess メソッドで Action を渡す

引数に Action を受け取り、前後にメッセージを出して実行するサンプルです。

using System;

class Program
{
    static void RunProcess(Action action)
    {
        Console.WriteLine("処理開始");
        action();
        Console.WriteLine("処理終了");
    }

    static void Main()
    {
        RunProcess(() =>
        {
            Console.WriteLine("中で行う処理です。");
        });

        RunProcess(() =>
        {
            Console.WriteLine("別の処理を実行しています。");
        });
    }
}

実行結果

処理開始
中で行う処理です。
処理終了
処理開始
別の処理を実行しています。
処理終了

まとめ

  • 独自イベントは event Action で追加できる
  • イベントハンドラを複数登録した場合、登録順に実行される
  • RunProcess に渡すことで、処理の前後に共通処理を差し込める

この3問で、イベントの仕組み と Actionの応用 が理解できます。


6. まとめ

  • Action は「処理を入れておける箱」
  • a(); で呼び出せる(a.Invoke(); も同じ意味)
  • イベントは「ボタンが押されたら登録した処理をまとめて呼ぶ仕組み」
  • 図で見ると「ボタン → イベント → 複数の処理」の流れが直感的にわかる


event と += の説明は、実際の .NET WinForms テンプレートにそのまま当てはめられます。ポイントは「イベントに購読(+=)して、イベント発生時にフレームワークが Invoke を代行する」という仕組みです。


1. WinForms プロジェクトのファイル構成

  • Program.cs … エントリポイントApplicationConfiguration.Initialize(); → Application.Run(new Form1());
  • Form1.cs … フォームの本体。イベントハンドラの中身を書く場所。
  • Form1.Designer.cs … Visual Studio デザイナが自動生成。コントロール生成・配置・イベント購読 += のコードがここに入る。→ 手動編集は避けるのが原則。

2. 自動生成されるイベント購読コードの違い

● .NET Framework 時代(古いテンプレート)

this.button1.Click += new System.EventHandler(this.button1_Click);
  • this. を明示
  • new System.EventHandler(…) を明示

● .NET 6 / 7 / 8 の現行テンプレート

button1.Click += button1_Click;
  • this. が省略される
  • new EventHandler(…) も省略(メソッドグループ変換で暗黙にキャスト)

👉 意味は完全に同じです。

現在のテンプレートは Roslyn によるコード生成がモダン化され、省略形が標準になっています。


3. Designer と記事コードの対応

記事の例:

button.Clicked += () => Console.WriteLine("処理1");
button.Clicked += () => Console.WriteLine("処理2");
button.Clicked?.Invoke();

WinForms の場合:

  • 購読 (+=)→ Form1.Designer.cs 内で button1.Click += button1_Click; が生成される。
  • イベント本体→ Form1.cs 内で private void button1_Click(object sender, EventArgs e) が作られる。
  • Invoke→ Clicked?.Invoke() は WinForms の内部 (OnClick) が代わりにやってくれる。開発者は直接書かない。

まとめ

  • 記事の event/+=/?.Invoke() の仕組みは、WinForms の Click でもそのまま適用できる。
  • 違いは「Invoke を自分で呼ばず、WinForms が内部でやってくれる」こと。
  • 自動生成される購読コードは
    • .NET Framework → this.button1.Click += new System.EventHandler(this.button1_Click);
    • .NET 6+ → button1.Click += button1_Click;と書式が異なるが、意味は同じ

👉 現在の Visual Studio / .NET でプロジェクトを作ると「省略形」で出ます。

昔の記事や教材に「new System.EventHandler(…)」と書いてあっても、内容は同じなので安心してください。


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