基礎から学ぶC#入門 90日コース | 実践力を高める - Day 66:delegate・ラムダ式

Web APP 90日で身につけるC#
スポンサーリンク
スポンサーリンク

Day 66 のゴールと全体像

Day 66 では、C# の“ちょっと大人な機能”である delegate・ラムダ式 に入門していきます。 今日のキーワードは次の4つです。

  • ラムダ式
  • Func
  • Action
  • コールバックの基礎

どれも、

「メソッドそのものを“値”として扱う」

ための道具です。 最初は少し不思議な感じがするかもしれませんが、 一度イメージがつかめると、コードの表現力がぐっと広がっていきます。

ここでは、

  • ラムダ式って何者?
  • Func / Action はどう使う?
  • コールバックってどういう場面で出てくる?

という流れを、例題コードを交えながら、 ステップバイステップでかみ砕いて説明していきます。

「メソッドを値として扱う」という発想

メソッドを変数に入れるってどういうこと?

ふつう、メソッドはこう書きますよね。

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

そして、呼び出すときはこうです。

int result = Add(3, 5);
C#

ここまでは「メソッドは呼び出すもの」という感覚だと思います。

でも、C# では

「メソッドそのものを変数に入れて、あとから呼び出す」

ということができます。

この「メソッドを変数に入れる」ための仕組みが delegate であり、 それをもっと使いやすくしたものが Func / Action / ラムダ式 です。

ラムダ式とは何か

ラムダ式の見た目

ラムダ式は、

「その場でサッと書ける“匿名メソッド”」

のようなものです。

いちばんシンプルな形は、

x => x * 2
C#

という書き方です。

これは、

  • 引数:x
  • 処理:x * 2 を返す

という「小さなメソッド」を、その場で書いているイメージです。

もう少し丁寧な形

(x, y) => x + y
C#

これは、

  • 引数:xy
  • 処理:x + y を返す

というメソッドです。

さらに、処理が複数行になる場合は、

(x, y) =>
{
    int sum = x + y;
    Console.WriteLine($"合計は {sum} です。");
    return sum;
}
C#

のように、波括弧 {} を使って書くこともできます。

ラムダ式のざっくりしたイメージ

ラムダ式は、

「名前を付けずに、その場でメソッドを書くための記法」

だと思ってもらうと、かなりイメージしやすくなります。

Func と Action の基本

Func とは

Func は、

「引数があって、戻り値もあるメソッド」を表すための型

です。

書き方は少し独特ですが、 慣れてしまえば「パターン」として見えてきます。

例:2つの int を受け取って、int を返す Func

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

ここでの意味は、

  • Func<int, int, int>
    • 最後の int が「戻り値の型」
    • それより前の int, int が「引数の型」
  • (x, y) => x + y
    • 「x と y を受け取って、x + y を返すラムダ式」

という形です。

呼び出すときは、

int result = add(3, 5);
Console.WriteLine($"結果: {result}"); // 8 が表示される
C#

と、ふつうのメソッドと同じように add(…) と書きます。

Action とは

Action は、

「引数はあるけれど、戻り値がないメソッド」を表すための型

です。

例:string を受け取って、表示するだけの Action

Action<string> print = message =>
{
    Console.WriteLine($"メッセージ: {message}");
};
C#

ここでの意味は、

  • Action<string>
    • 引数の型が string
    • 戻り値はなし(void のイメージ)
  • message => { ... }
    • 「message を受け取って、Console.WriteLine する」

という形です。

呼び出すときは、

print("こんにちは");
C#

と書くだけです。

Func と Action の違いを整理

  • Func
    • 「戻り値あり」のメソッドを表す
    • 最後の型が戻り値、それより前が引数
  • Action
    • 「戻り値なし」のメソッドを表す
    • 型パラメータは引数だけ

この2つを覚えておくと、 「メソッドを変数に入れて扱う」場面で、とても役に立ちます。

ラムダ式 + Func / Action を組み合わせてみる

例:配列の要素を加工する処理を差し替える

同じ配列に対して、

  • 2倍にする
  • 3倍にする
  • マイナスにする

など、いろいろな加工をしたくなることがあります。

そんなときに、 「加工の仕方」をラムダ式で渡せると、とても柔軟になります。

using System;

class Program
{
    static void Main()
    {
        int[] numbers = { 1, 2, 3, 4, 5 };

        // 2倍にする処理
        Console.WriteLine("2倍にした結果:");
        ProcessArray(numbers, x => x * 2);

        // 3倍にする処理
        Console.WriteLine("3倍にした結果:");
        ProcessArray(numbers, x => x * 3);

        // マイナスにする処理
        Console.WriteLine("マイナスにした結果:");
        ProcessArray(numbers, x => -x);
    }

    // 配列の各要素に「加工処理」を適用して表示するメソッド
    static void ProcessArray(int[] array, Func<int, int> processor)
    {
        foreach (int n in array)
        {
            int result = processor(n); // 渡された処理を適用
            Console.WriteLine(result);
        }
    }
}
C#

ここでのポイントは、

  • ProcessArray が「配列を処理する枠組み」を提供している
  • 実際に「どう加工するか」は、Func<int, int> processor に任せている
  • 呼び出し側で、ラムダ式 x => x * 2 などを渡している

というところです。

「枠組み」と「具体的な処理」を分けて書けるようになる

のが、ラムダ式 + Func の大きな魅力です。

コールバックの基礎

コールバックって何?

コールバックは、

「ある処理が終わったタイミングで、呼び出してほしいメソッド」

のことです。

たとえば、

  • 「時間のかかる処理が終わったら、結果を表示したい」
  • 「ボタンが押されたら、この処理を実行したい」

といった場面で、 「終わったときに呼んでね」「押されたときに呼んでね」と メソッドを“登録”しておくイメージです。

例:処理が終わったらメッセージを表示するコールバック

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("長い処理を開始します…");

        // コールバックとして、処理完了時に呼んでほしいメソッドを渡す
        await DoLongWorkAsync(OnWorkCompleted);

        Console.WriteLine("Main の処理も終わりました。");
    }

    // 時間のかかる処理
    static async Task DoLongWorkAsync(Action<string> callback)
    {
        Console.WriteLine("処理中…");
        await Task.Delay(2000); // 2秒待つ(長い処理のイメージ)
        Console.WriteLine("処理が終わりました。");

        // 終わったタイミングでコールバックを呼ぶ
        callback("処理が正常に完了しました。");
    }

    // コールバックとして呼ばれるメソッド
    static void OnWorkCompleted(string message)
    {
        Console.WriteLine($"コールバック: {message}");
    }
}
C#

ここでの流れは、

  1. Main から DoLongWorkAsync(OnWorkCompleted) を呼ぶ
    • 「処理が終わったら OnWorkCompleted を呼んでね」と渡している
  2. DoLongWorkAsync の中で、2秒待つ
  3. 終わったタイミングで callback("…") を呼ぶ
    • 実際には OnWorkCompleted("…") が呼ばれる
  4. OnWorkCompleted がメッセージを表示する

という形になっています。

ラムダ式をコールバックとして渡す

コールバックは、 わざわざ別メソッドを定義しなくても、 ラムダ式でその場で書いて渡すこともできます。

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("長い処理を開始します…");

        await DoLongWorkAsync(message =>
        {
            Console.WriteLine($"ラムダ式コールバック: {message}");
        });

        Console.WriteLine("Main の処理も終わりました。");
    }

    static async Task DoLongWorkAsync(Action<string> callback)
    {
        Console.WriteLine("処理中…");
        await Task.Delay(2000);
        Console.WriteLine("処理が終わりました。");

        callback("処理が正常に完了しました。");
    }
}
C#

このように、

  • コールバックの枠組みは Action<string> で用意しておき
  • 呼び出し側で「何をしたいか」をラムダ式で渡す

というスタイルは、 実践的なコードで非常によく使われます。

delegate から Func / Action への橋渡し

伝統的な delegate の書き方

C# には、もともと delegate というキーワードがあります。

// int を受け取って int を返すメソッドを表す delegate 型
delegate int IntOperation(int x);
C#

これを使うと、

using System;

class Program
{
    static void Main()
    {
        IntOperation op = Double;

        int result = op(10);
        Console.WriteLine($"結果: {result}");
    }

    static int Double(int x)
    {
        return x * 2;
    }
}
C#

という書き方ができます。

ただ、

  • 型ごとに delegate を定義する必要がある
  • 少し記述が重くなりがち

という理由から、 今は Func / Action + ラムダ式 を使うスタイルが主流です。

Func / Action は「汎用的な delegate」

  • Func<T1, T2, TResult>
  • Action<T1, T2>

といった型は、

「よく使うパターンの delegate を、あらかじめ用意してくれているもの」

だと思ってもらうと、しっくり来ると思います。

Day 66 用ミニテンプレート

最後に、今日の内容をそのまま使える形のテンプレートとしてまとめておきます。

ラムダ式 + Func の基本形

// 2つの int を受け取って、int を返す処理
Func<int, int, int> add = (x, y) => x + y;

int result = add(3, 5);
Console.WriteLine($"結果: {result}");
C#

ラムダ式 + Action の基本形

// string を受け取って、表示するだけの処理
Action<string> print = message =>
{
    Console.WriteLine($"メッセージ: {message}");
};

print("こんにちは");
C#

コールバックの基本形

static async Task Main()
{
    await DoWorkAsync(message =>
    {
        Console.WriteLine($"コールバックで受け取ったメッセージ: {message}");
    });
}

static async Task DoWorkAsync(Action<string> callback)
{
    Console.WriteLine("処理中…");
    await Task.Delay(1000);
    Console.WriteLine("処理が終わりました。");

    callback("処理完了!");
}
C#

Day 66 のまとめ

Day 66 では、

  • ラムダ式(その場で書ける“匿名メソッド”)
  • Func(戻り値ありのメソッドを表す型)
  • Action(戻り値なしのメソッドを表す型)
  • コールバック(「終わったときに呼んでね」というメソッドの登録)

という、C# の“メソッドを値として扱う”ための道具たちを、 例題コードとともにじっくり見ていきました。

最初は少し抽象的に感じるかもしれませんが、

  • 小さなラムダ式を書いてみる
  • Func / Action を使って「処理の差し替え」を試してみる
  • コールバックの枠組みを作って、ラムダ式で中身を渡してみる

といった練習を重ねていくうちに、 「メソッドを渡す」「メソッドを登録する」という感覚が、 自然と手になじんでいきます。

ぜひ、ラムダ式や Func / Action を、

「コードの柔軟さと表現力を一段引き上げてくれる、頼れる相棒」

として、今後のプログラミングに活かしていっていただければうれしいです。

タイトルとURLをコピーしました