bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] 匿名メソッドを理解する

[C#] 匿名メソッドを理解する

Jun 17, 2026 C# , .NET bucket-sort

前回は、C# のイベントについて見ました。

今回は、イベント登録やコールバックで使える匿名メソッドを整理します。

匿名メソッドは、名前付きメソッドを別に用意せず、その場で処理を書くための構文です。

現在の C# ではラムダ式を使う場面が多いですが、匿名メソッドを理解しておくと、デリゲートとラムダ式の関係も見えやすくなります。

匿名メソッドとは

匿名メソッドは、delegate キーワードを使ってその場でメソッド本体を書く構文です。

たとえば、次のような名前付きメソッドがあるとします。

using System;

class Program
{
    static void Main()
    {
        Action<string> printer = Print;

        printer("Hello");
    }

    static void Print(string message)
    {
        Console.WriteLine(message);
    }
}

この Print メソッドを別に定義せず、代入する場所で直接書けます。

using System;

class Program
{
    static void Main()
    {
        Action<string> printer = delegate (string message)
        {
            Console.WriteLine(message);
        };

        printer("Hello");
    }
}

実行結果です。

Hello

この部分が匿名メソッドです。

delegate (string message)
{
    Console.WriteLine(message);
}

名前はありませんが、Action<string> に代入できるメソッドとして扱われます。

イベント登録で使う

匿名メソッドは、イベントハンドラーをその場で書きたいときにも使えます。

using System;

class Worker
{
    public event EventHandler? Completed;

    public void Run()
    {
        Console.WriteLine("処理中...");
        Completed?.Invoke(this, EventArgs.Empty);
    }
}

class Program
{
    static void Main()
    {
        Worker worker = new Worker();

        worker.Completed += delegate (object? sender, EventArgs e)
        {
            Console.WriteLine("処理が完了しました");
        };

        worker.Run();
    }
}

実行結果です。

処理中...
処理が完了しました

一度しか使わない短いイベントハンドラーであれば、名前付きメソッドを作らずに済みます。

ただし、あとで -= によって購読解除したい場合は注意が必要です。

worker.Completed += delegate (object? sender, EventArgs e)
{
    Console.WriteLine("完了");
};

worker.Completed -= delegate (object? sender, EventArgs e)
{
    Console.WriteLine("完了");
};

この 2 つは、同じ内容に見えても別の匿名メソッドです。

そのため、購読解除したい場合は、変数に入れてから登録します。

EventHandler handler = delegate (object? sender, EventArgs e)
{
    Console.WriteLine("完了");
};

worker.Completed += handler;
worker.Completed -= handler;

ラムダ式との関係

匿名メソッドは、ラムダ式に近い仕組みです。

次の匿名メソッドは、

Action<string> printer = delegate (string message)
{
    Console.WriteLine(message);
};

ラムダ式では次のように書けます。

Action<string> printer = message =>
{
    Console.WriteLine(message);
};

さらに 1 行で書くこともできます。

Action<string> printer = message => Console.WriteLine(message);

現在はラムダ式の方がよく使われます。

ただし、匿名メソッドは「デリゲートにその場でメソッド本体を渡している」という構造が見えやすい構文です。

ローカル変数にアクセスする

匿名メソッドは、外側のローカル変数にアクセスできます。

using System;

class Program
{
    static void Main()
    {
        int count = 0;

        Action increment = delegate
        {
            count++;
            Console.WriteLine($"count = {count}");
        };

        increment();
        increment();
    }
}

実行結果です。

count = 1
count = 2

匿名メソッドの外側で宣言した count に、匿名メソッドの内側からアクセスしています。

このように、匿名メソッドやラムダ式が外側の変数を使うことを、キャプチャと呼びます。

キャプチャされた変数は共有される

キャプチャされた変数は、値がコピーされるだけではありません。

匿名メソッドと外側のコードが、同じ変数を共有します。

using System;

class Program
{
    static void Main()
    {
        int value = 10;

        Action show = delegate
        {
            Console.WriteLine(value);
        };

        value = 20;

        show();
    }
}

実行結果です。

20

匿名メソッドを作った時点では value は 10 でした。

しかし、その後に value を 20 に変更してから show() を呼ぶと、20 が表示されます。

つまり、匿名メソッドは作成時点の値ではなく、変数そのものを参照しています。

ループ変数を使うときの注意

キャプチャでよく注意が必要なのは、ループ変数です。

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<Action> actions = new List<Action>();

        for (int i = 0; i < 3; i++)
        {
            actions.Add(delegate
            {
                Console.WriteLine(i);
            });
        }

        foreach (Action action in actions)
        {
            action();
        }
    }
}

実行結果です。

3
3
3

0, 1, 2 ではなく、3 が 3 回表示されます。

これは、各匿名メソッドが同じ i を参照しているためです。

意図した値を保持したい場合は、ループ内で別のローカル変数に入れます。

for (int i = 0; i < 3; i++)
{
    int current = i;

    actions.Add(delegate
    {
        Console.WriteLine(current);
    });
}

この場合は、次のように表示されます。

0
1
2

static 匿名メソッドを使う

C# 9.0 では、匿名関数に static を付けられるようになりました。

匿名メソッドでも、次のように書けます。

using System;

class Program
{
    static void Main()
    {
        Action<string> printer = static delegate (string message)
        {
            Console.WriteLine(message);
        };

        printer("Hello");
    }
}

static を付けた匿名メソッドは、外側のローカル変数やインスタンスメンバーをキャプチャできません。

using System;

class Program
{
    static void Main()
    {
        string prefix = "LOG:";

        Action<string> logger = static delegate (string message)
        {
            Console.WriteLine($"{prefix} {message}"); // コンパイルエラー
        };
    }
}

prefix は外側のローカル変数なので、static 匿名メソッドの中では使えません。

これは不便に見えるかもしれませんが、意図しないキャプチャを防げるという利点があります。

ラムダ式でも同じように static を付けられます。

Func<int, int> square = static x => x * x;

キャプチャが不要な処理では、static を付けることで「外側の状態に依存しない処理である」と明示できます。

匿名メソッドで破棄パラメーターを使う

C# 9.0 では、匿名関数のパラメーターに破棄を使いやすくなりました。

破棄とは、使わない値を _ で受ける書き方です。

たとえば、イベントハンドラーでは sender や EventArgs を使わないことがあります。

button.Click += delegate (object? _, EventArgs _)
{
    Console.WriteLine("クリックされました");
};

このように _ を使うと、「この引数は使わない」という意図を表せます。

複数の引数をどちらも使わない場合でも、破棄としての _ は同じ名前を複数回使えます。

Func<int, int, int> constant = delegate (int _, int _)
{
    return 42;
};

ラムダ式でも同じ考え方で書けます。

Func<int, int, int> constant = (_, _) => 42;

破棄パラメーターを使うと、使っていない引数に名前を付けなくて済むため、コードの意図がすっきりします。

実践例: イベント登録を匿名メソッドで書く

最後に、イベントと匿名メソッドを組み合わせた例を見ます。

using System;

class ProgressChangedEventArgs : EventArgs
{
    public int Percent { get; }

    public ProgressChangedEventArgs(int percent)
    {
        Percent = percent;
    }
}

class Downloader
{
    public event EventHandler<ProgressChangedEventArgs>? ProgressChanged;
    public event EventHandler? Completed;

    public void Download()
    {
        for (int percent = 0; percent <= 100; percent += 50)
        {
            ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(percent));
        }

        Completed?.Invoke(this, EventArgs.Empty);
    }
}

class Program
{
    static void Main()
    {
        Downloader downloader = new Downloader();

        string fileName = "report.pdf";

        downloader.ProgressChanged += delegate (object? sender, ProgressChangedEventArgs e)
        {
            Console.WriteLine($"{fileName}: {e.Percent}%");
        };

        downloader.Completed += static delegate (object? _, EventArgs _)
        {
            Console.WriteLine("ダウンロードが完了しました");
        };

        downloader.Download();
    }
}

実行結果です。

report.pdf: 0%
report.pdf: 50%
report.pdf: 100%
ダウンロードが完了しました

ProgressChanged のハンドラーは、外側の fileName を使っているためキャプチャしています。

一方、Completed のハンドラーは外側の変数を使わないため、static delegate として書いています。

まとめ

匿名メソッドは、名前付きメソッドを作らずに、デリゲートへその場で処理を渡すための構文です。

  • delegate キーワードで匿名メソッドを書ける
  • イベント登録やコールバックで使える
  • 外側のローカル変数にアクセスできる
  • 外側の変数を使うことをキャプチャと呼ぶ
  • キャプチャされた変数は、値のコピーではなく変数そのものとして共有される
  • C# 9.0 では static 匿名メソッドでキャプチャを禁止できる
  • C# 9.0 では、使わない引数を破棄パラメーター _ として表しやすくなった

現在はラムダ式を使うことが多いですが、匿名メソッドはデリゲートの仕組みを理解するうえで重要です。

イベント、デリゲート、匿名メソッド、ラムダ式はそれぞれ別の機能に見えますが、どれも「処理を値として扱う」という考え方でつながっています。

C# .NET 匿名メソッド 匿名関数 ラムダ式 デリゲート C# 9.0
← [C#] イベントを理解する: event キーワードと EventHandler [C#] ラムダ式を理解する →

Related Posts

  • [C#] ラムダ式を理解する Jun 18, 2026
  • [C#] LINQ to Objects を理解する: クエリの内部表現 Jun 22, 2026
  • [C#] デリゲートを理解する: Action と Func Jun 15, 2026
  • [C#] イベントを理解する: event キーワードと EventHandler Jun 16, 2026

Table of Contents

  • 匿名メソッドとは
  • イベント登録で使う
  • ラムダ式との関係
  • ローカル変数にアクセスする
  • キャプチャされた変数は共有される
  • ループ変数を使うときの注意
  • static 匿名メソッドを使う
  • 匿名メソッドで破棄パラメーターを使う
  • 実践例: イベント登録を匿名メソッドで書く
  • まとめ

Recent Posts

  • [C#] プラグインで拡張できるアプリケーションを設計する Jul 9, 2026
  • [C#] 属性でコードに意味を与える Jul 8, 2026
  • [C#] 外部アセンブリを読み込み実行時に型を呼び出す Jul 7, 2026
  • [C#] リフレクションで型の中身を調べる Jul 6, 2026
  • [C#] 型メタデータとリフレクションの基礎を理解する Jul 5, 2026

Categories

  • C#120
  • .NET119
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • System.Collections.Generic
  • デリゲート
  • Code Snippet
  • DynamoDB
  • LINQ
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メモリ管理
  • リフレクション
  • DoS
  • Nginx
  • WordPress
  • メタデータ
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • インターフェース
  • オブジェクト指向
  • クラス設計
  • デザインパターン
  • パターンマッチング
  • ポインター
  • 継承
Powered by Hugo & Explore Theme.