bucket-sort logo bucket-sort

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

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] デリゲートを理解する: Action と Func

[C#] デリゲートを理解する: Action と Func

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

前回は、デリゲートを使った状態通知とマルチキャストを見ました。

今回は、C# で標準的に使われるジェネリックデリゲートである Action<> と Func<> を整理します。

デリゲートは自分で定義できます。

delegate void MessageHandler(string message);
delegate int Calculator(int x, int y);

しかし、よく使う形のデリゲートは .NET にあらかじめ用意されています。

代表的なものが、Action<> と Func<> です。

ジェネリックデリゲートとは

ジェネリックデリゲートは、型引数によって引数や戻り値の型を指定できるデリゲートです。

たとえば、次の独自デリゲートを考えます。

delegate void LogHandler(string message);

これは、string を 1 つ受け取り、戻り値がないメソッドを表します。

同じ形は、Action<string> で表せます。

Action<string> logger;

また、次の独自デリゲートを考えます。

delegate int Calculator(int x, int y);

これは、int を 2 つ受け取り、int を返すメソッドを表します。

同じ形は、Func<int, int, int> で表せます。

Func<int, int, int> calculator;

Action は戻り値がない処理を表す

Action は、戻り値がない処理を表すデリゲートです。

引数がない場合は、Action を使います。

using System;

class Program
{
    static void Main()
    {
        Action action = SayHello;

        action();
    }

    static void SayHello()
    {
        Console.WriteLine("Hello");
    }
}

実行結果です。

Hello

Action は、次の独自デリゲートに近い意味を持ちます。

delegate void SomeAction();

Action

引数を 1 つ持つ場合は、Action<T> を使います。

using System;

class Program
{
    static void Main()
    {
        Action<string> logger = WriteLog;

        logger("処理を開始しました");
    }

    static void WriteLog(string message)
    {
        Console.WriteLine($"LOG: {message}");
    }
}

実行結果です。

LOG: 処理を開始しました

Action<string> は、次の形のメソッドを表します。

  • string を 1 つ受け取る
  • 戻り値は void

独自デリゲートで書くなら、次と同じ形です。

delegate void LogHandler(string message);

Action<T1, T2>

引数が複数ある場合も Action を使えます。

using System;

class Program
{
    static void Main()
    {
        Action<string, int> printUser = PrintUser;

        printUser("Alice", 25);
    }

    static void PrintUser(string name, int age)
    {
        Console.WriteLine($"{name} は {age} 歳です");
    }
}

実行結果です。

Alice は 25 歳です

Action<string, int> は、string と int を受け取り、戻り値がない処理を表します。

Func は戻り値がある処理を表す

Func は、戻り値がある処理を表すデリゲートです。

Func では、最後の型引数が戻り値の型になります。

Func<int, int, int>

これは、次の形のメソッドを表します。

  • 1 つ目の引数が int
  • 2 つ目の引数が int
  • 戻り値が int

例を見てみます。

using System;

class Program
{
    static void Main()
    {
        Func<int, int, int> calculator = Add;

        int result = calculator(10, 20);

        Console.WriteLine(result);
    }

    static int Add(int x, int y)
    {
        return x + y;
    }
}

実行結果です。

30

独自デリゲートで書くなら、次と同じ形です。

delegate int Calculator(int x, int y);

Func

引数がなく、戻り値だけがある場合は Func<TResult> を使います。

using System;

class Program
{
    static void Main()
    {
        Func<DateTime> getNow = GetCurrentTime;

        Console.WriteLine(getNow());
    }

    static DateTime GetCurrentTime()
    {
        return DateTime.Now;
    }
}

Func<DateTime> は、引数を取らず、DateTime を返す処理を表します。

ラムダ式と組み合わせる

Action や Func は、ラムダ式とよく組み合わせて使います。

Action<string> logger = message =>
{
    Console.WriteLine($"LOG: {message}");
};

logger("ラムダ式から出力");

Func の例です。

Func<int, int, int> multiply = (x, y) =>
{
    return x * y;
};

Console.WriteLine(multiply(3, 4));

実行結果です。

12

戻り値をそのまま返すだけなら、さらに短く書けます。

Func<int, int, int> multiply = (x, y) => x * y;

ラムダ式を使うと、別メソッドを定義しなくても、その場で処理を渡せます。

実践例: フィルター処理を差し替える

Func<T, bool> は、条件判定を表すときによく使われます。

次の例では、リストから条件に合う値だけを取り出します。

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

        List<int> evenNumbers = Filter(numbers, IsEven);
        List<int> largeNumbers = Filter(numbers, n => n >= 4);

        Console.WriteLine(string.Join(", ", evenNumbers));
        Console.WriteLine(string.Join(", ", largeNumbers));
    }

    static List<int> Filter(List<int> source, Func<int, bool> predicate)
    {
        List<int> result = new List<int>();

        foreach (int item in source)
        {
            if (predicate(item))
            {
                result.Add(item);
            }
        }

        return result;
    }

    static bool IsEven(int number)
    {
        return number % 2 == 0;
    }
}

実行結果です。

2, 4, 6
4, 5, 6

Filter メソッドは、条件そのものを知りません。

条件は、引数として渡された Func<int, bool> に任せています。

static List<int> Filter(List<int> source, Func<int, bool> predicate)

このようにすると、処理の流れは共通化しつつ、判定ロジックだけを差し替えられます。

実践例: 処理完了時のコールバック

Action は、処理完了時のコールバックにも使えます。

using System;

class Program
{
    static void Main()
    {
        DownloadFile(
            "report.pdf",
            fileName => Console.WriteLine($"{fileName} のダウンロードが完了しました")
        );
    }

    static void DownloadFile(string fileName, Action<string> completed)
    {
        Console.WriteLine($"{fileName} をダウンロード中...");

        // 実際にはここでダウンロード処理を行う

        completed(fileName);
    }
}

実行結果です。

report.pdf をダウンロード中...
report.pdf のダウンロードが完了しました

DownloadFile は、完了後に何をするかを知りません。

完了後の処理は、Action<string> として外部から受け取っています。

Action と Func の使い分け

基本的な使い分けはシンプルです。

デリゲート 使う場面
Action / Action<> 戻り値がない処理
Func<> 戻り値がある処理

例です。

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

Func<int, int, int> add = (x, y) => x + y;

戻り値がないなら Action、戻り値があるなら Func と考えるとよいでしょう。

独自 delegate と Action / Func の使い分け

Action や Func があれば、独自のデリゲート型は不要に見えるかもしれません。

しかし、独自デリゲートにも意味があります。

たとえば、次のデリゲートを見てみます。

delegate void OrderStatusChangedHandler(Order order);

これは、技術的には次のように書けます。

Action<Order>

ただし、OrderStatusChangedHandler という名前があることで、「注文状態が変わったときの通知である」という意味がコード上に残ります。

一方、単純なコールバックや小さな処理の受け渡しであれば、Action / Func の方が簡潔です。

目安は次の通りです。

場面 選択肢
汎用的な処理を渡したい Action / Func
ドメイン上の意味を名前で表したい 独自 delegate
イベントとして公開したい 独自 delegate または EventHandler 系

まとめ

Action<> と Func<> は、.NET が標準で用意しているジェネリックデリゲートです。

  • Action は戻り値がない処理を表す
  • Func は戻り値がある処理を表す
  • Func では最後の型引数が戻り値の型になる
  • ラムダ式と組み合わせると、その場で処理を渡せる
  • 条件判定には Func<T, bool> がよく使われる
  • 完了通知などのコールバックには Action<T> が使いやすい
  • 意味のある名前を残したい場合は、独自 delegate も有効

デリゲートを理解すると、C# のイベント、ラムダ式、LINQ、非同期処理のコールバックなどが読みやすくなります。

まずは、戻り値がなければ Action、戻り値があれば Func と考えるところから始めるとよいでしょう。

C# .NET Delegate デリゲート Action Func ラムダ式
← [C#] デリゲートを理解する: 状態通知とマルチキャスト [C#] イベントを理解する: event キーワードと EventHandler →

Related Posts

  • [C#] ラムダ式を理解する Jun 18, 2026
  • [C#] LINQ to Objects を理解する: クエリの内部表現 Jun 22, 2026
  • [C#] 匿名メソッドを理解する Jun 17, 2026
  • [C#] デリゲートを理解する: 状態通知とマルチキャスト Jun 14, 2026

Table of Contents

  • ジェネリックデリゲートとは
  • Action は戻り値がない処理を表す
  • Action
  • Action<T1, T2>
  • Func は戻り値がある処理を表す
  • Func
  • ラムダ式と組み合わせる
  • 実践例: フィルター処理を差し替える
  • 実践例: 処理完了時のコールバック
  • Action と Func の使い分け
  • 独自 delegate と Action / Func の使い分け
  • まとめ

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.