【C#初心者必見】C#のラムダ式を使いこなそう!わかりやすいサンプルコード満載

C#プログラミングを学び始めると、=> という見慣れない記号を使ったコードに出会うことがあります。

これが「ラムダ式」と呼ばれるもので、C#の強力な機能の一つです。
しかし、初心者にとっては
「何これ?」
「どうやって使うの?」
「そもそも何のためにあるの?」
と疑問に思うことが多いかもしれません。

この記事ではそんなC#初心者の方に向けて、
ラムダ式の基本から実践的な使い方まで、わかりやすく丁寧に解説します。

ラムダ式はコードを短く、そして読みやすくするために非常に役立ちます。
特に、データの問い合わせを行うLINQ(Language Integrated Query)を使う際には、ほぼ必須と言える知識です。

難しそうに見えるラムダ式も、基本から順を追って学べば必ず理解できます。

記事のポイント

  • C# ラムダ式の基本的な概念とメリットがわかる
  • ラムダ式の具体的な書き方(構文)を学べる
  • 引数、戻り値、変数、複数行など、様々なパターンのラムダ式を理解できる
  • if文を含むラムダ式の書き方がわかる
  • デリゲートとの関係性やLINQでの活用法がわかる
  • 具体的な使いどころをサンプルコードで確認できる

\ 2025年注目のフリーランスエージェント3社比較 /

収入を上げたい会社員」や「フリーランスでも安定して稼ぎたい人」にはフリーランスエージェントがオススメです。

スクロールできます
フリーランス
エージェント
特徴オススメ度リンク
登録者No1!!IT・Web業界特化のエージェントサービス
リモートでの参画率91%以上
迷ったらここで間違いなし
市場分析ツールで案件数の推移等が見れる

詳細ページ
給与保障制度あり
保障が正社員並みだから、会社員から転向する人は特にオススメ!
フリーランスの働き方と正社員並みの保障というイイトコどりができる!
リモートワーク案件に強み

詳細ページ
契約や手数料が全てオープンで手数料が安くなる仕組みがあるから、手数料が気になる人にオススメ!
健康面や教育面でのサポートも充実
業務系案件に強み

詳細ページ
複数サイトで情報収集するのがオススメ!

目次

【C#】ラムダ式の基礎知識

まずは、C#のラムダ式とは一体何なのか、基本的なところから見ていきましょう。
なぜラムダ式が必要なのか、どう書くのか、そして関連する概念について解説していきます。

ラムダ式とは?まずは基本をわかりやすく解説

プログラミングにおけるラムダ式とは、簡単に言うと「名前のない関数」のことです。
「名前のない関数」って??となりますよね。

これは別名「匿名関数」とも呼ばれ、
通常のメソッド(関数)は、public void MyMethod() { ... } のように名前を付けて定義しますが、
ラムダ式はその場で短い処理を記述するために使われ、名前を付ける必要がありません。

C#では、=> という演算子(ラムダ演算子、アロー演算子とも呼ばれます)を使ってラムダ式を表現します。
この演算子の左側に引数リスト、右側に処理内容を書くのが基本です。

// 例: 引数を2倍にするラムダ式
(int x) => x * 2;

このように、メソッドを定義する手間を省き、コードをより簡潔に書けるのがラムダ式の大きな特徴です。
特に、他のメソッドの引数として小さな処理を渡したい場合などに非常に便利です。

ラムダ式は何のために使うのですか?メリットを理解しよう

ラムダ式を学ぶことには、いくつかの重要なメリットがあります。

  1. コードの簡潔化:
    メソッドをわざわざ定義しなくても、
    その場で処理を記述できるため、コード量が大幅に削減されます。
    特に短い処理の場合、その効果は絶大です。
  2. 可読性の向上:
    処理の内容がその場でわかるため、コードの流れを追いやすくなる場合があります。
    (ただし、複雑すぎるラムダ式は逆に可読性を下げることもあります。)
  3. LINQとの強力な連携:
    C#の強力なデータ操作機能であるLINQでは、
    データのフィルタリング、変換、ソートなどの条件を指定するためにラムダ式が頻繁に使われます。
    LINQを使いこなすにはラムダ式の理解が不可欠です。
  4. イベントハンドラや非同期処理の簡略化:
    ボタンクリック時の処理(イベントハンドラ)や、
    時間のかかる処理を別スレッドで行う非同期処理なども、ラムダ式を使うとシンプルに記述できます。

これらのメリットにより、現代的なC#プログラミングにおいてラムダ式は欠かせない要素となっています。

ラムダ式の基本的な書き方(構文)をマスターしよう

ラムダ式の基本的な構文は以下の形式です。

(引数リスト) => { 処理内容 }

または、処理内容が単一の式で表現できる場合は、
中括弧 {}return キーワード(戻り値がある場合)を省略できます。

(引数リスト) => 式

いくつかのパターンを見てみましょう。

引数がない場合: () を使う

Action printHello = () => Console.WriteLine("Hello!"); 
printHello(); // "Hello!" と出力される

引数が1つの場合: 引数の型を省略でき、さらに括弧 () も省略できる

// Func<int, int> は int を引数に取り int を返すデリゲート型
Func<int, int> doubleValue = x => x * 2; // 型も括弧も省略
Console.WriteLine(doubleValue(5)); // 10 と出力される

// 型を明示する場合
Func<int, int> tripleValue = (int x) => x * 3;
Console.WriteLine(tripleValue(5)); // 15 と出力される

引数が複数の場合: 括弧 () で引数リストを囲む

型は省略可能です(コンパイラが推論できる場合)。

// Func<int, int, int> は int 2つを引数に取り int を返すデリゲート型
Func<int, int, int> add = (x, y) => x + y; // 型を省略
Console.WriteLine(add(3, 4)); // 7 と出力される

// 型を明示する場合
Func<int, int, int> subtract = (int x, int y) => x - y;
Console.WriteLine(subtract(10, 3)); // 7 と出力される

このように、状況に応じて柔軟な書き方ができるのがラムダ式の特徴です。

C#のデリゲートとラムダ式の違いは何ですか?関係性を理解する

ラムダ式を理解する上で、「デリゲート」という概念が重要になります。

デリゲートとは、簡単に言うと「メソッド(関数)への参照を保持できる型」のことです。
メソッドを変数のように扱えるようにする仕組み、と考えるとわかりやすいかもしれません。

例えば、ActionFunc は、.NET Framework に標準で用意されている汎用的なデリゲート型です。

  • Action: 戻り値のないメソッドを参照するためのデリゲート。
  • Func: 戻り値のあるメソッドを参照するためのデリゲート。

では、ラムダ式とデリゲートの関係はどうなっているのでしょうか?

ラムダ式は、特定のデリゲート型のインスタンスを生成するための非常に簡潔な構文なのです。

コンパイラは、
ラムダ式がどのデリゲート型(例えば ActionFunc<int, int> など)に代入されようとしているかを見て、
そのデリゲート型に適合するメソッド(匿名メソッド)を内部的に生成し、
そのメソッドへの参照を持つデリゲートのインスタンスを作成します。

// ラムダ式 (x => x * 2) は、Func<int, int> 型のデリゲートに代入されている
Func<int, int> doubler = x => x * 2;

// これは、以下のような匿名メソッドを使ったデリゲートの生成と似ている(C# 2.0 の書き方)
Func<int, int> doubler_old = delegate(int x) { return x * 2; };

// さらに、以下のように名前付きメソッドを用意してデリゲートを生成するのと同じ目的を達成する
static int DoubleMethod(int x) { return x * 2; }
Func<int, int> doubler_named = DoubleMethod;

ラムダ式は、
このデリゲートの仕組みを背景に、より手軽に関数を扱えるようにした現代的な書き方と言えます。
デリゲートそのものを深く理解していなくても、
まずは ActionFunc と組み合わせてラムダ式を使えれば大丈夫です。

ラムダ式 いつから?登場した背景

C#のラムダ式は、C# 3.0 で導入されました。
これは、.NET Framework 3.5 と共にリリースされたバージョンです (2007年頃)。

ラムダ式が登場した最大の理由は、LINQ (Language Integrated Query) の導入です。
LINQは、データベース、XML、メモリ上のコレクションなど、
様々なデータソースに対して統一的な方法で問い合わせ(クエリ)を行うための機能です。

LINQのクエリ構文やメソッド構文では、
「どのような条件でデータを絞り込むか」
「どのようにデータを変換するか」
といった操作を、メソッドの引数として渡す必要がありました。

この「操作」を簡潔に記述するために、ラムダ式が最適な方法として同時に導入されたのです。
ラムダ式がなければ、LINQの記述はもっと冗長になっていたでしょう。

【C#】ラムダ式の実践テクニック

プログラミング

基本を理解したところで、次はラムダ式をより実践的に使いこなすためのテクニックを見ていきましょう。
引数や戻り値の扱い、複数行の処理、if文の使い方、そして具体的な活用シーンについて解説します。

引数あり/なしのラムダ式の書き方バリエーション

ラムダ式は、引数の数に応じて書き方が少し変わります。
基本的なデリゲート型である Action (戻り値なし) と Func (戻り値あり) を使って、
具体的なバリエーションを見てみましょう。

引数なし、戻り値なし (Action)

Action showMessage = () => Console.WriteLine("引数なしのラムダ式です。");
showMessage();

引数1つ、戻り値なし (Action<T>)

Action<string> greet = name => Console.WriteLine($"こんにちは、{name}さん!");
// 括弧は省略可能
// Action<string> greet = (name) => Console.WriteLine($"こんにちは、{name}さん!");

greet("山田"); // "こんにちは、山田さん!"

引数複数、戻り値なし (Action<T1, T2, …>)

Action<string, int> printInfo = (item, price) => Console.WriteLine($"{item}の価格は{price}円です。");
printInfo("リンゴ", 150); // "リンゴの価格は150円です。"

引数あり、戻り値あり (Func<T, TResult>, Func<T1, T2, TResult>, …)

// 引数1つ、戻り値あり
Func<double, double> calculateArea = radius => Math.PI * radius * radius;
Console.WriteLine($"半径5の円の面積: {calculateArea(5)}");

// 引数2つ、戻り値あり
Func<int, int, bool> isGreater = (a, b) => a > b;
Console.WriteLine($"10は5より大きいか: {isGreater(10, 5)}"); // True

これらのパターンを覚えておけば、様々な場面でラムダ式を活用できます。

戻り値があるラムダ式とないラムダ式の違いと使い方

ラムダ式には、
処理結果として値を返すもの(戻り値あり)と、返さないもの(戻り値なし)があります。
これは、対応するデリゲート型によって決まります。

戻り値なし

ActionAction<T> などの Action 系デリゲートに対応します。
主に、何かを表示したり、状態を変更したりする副作用を目的とする処理に使われます。

List<string> names = new List<string> { "Alice", "Bob", "Charlie" };

// 各要素をコンソールに出力する (戻り値なし)
names.ForEach(name => Console.WriteLine(name));

戻り値あり

Func<TResult>, Func<T, TResult> などの Func 系デリゲートに対応します。
計算結果や処理結果の値を取得したい場合に使われます。

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// 各要素を2倍した新しいリストを作成する (戻り値あり)
List<int> doubledNumbers = numbers.Select(n => n * 2).ToList();
// doubledNumbers は { 2, 4, 6, 8, 10 } になる

return キーワードの扱い

ラムダ式の処理内容が単一の式で表現できる場合(式形式ラムダ)、return キーワードは不要です。
コンパイラが自動的にその式の評価結果を戻り値として扱います。

Func<int, int> addOne = x => x + 1; // return は不要

一方、処理内容が複数行にわたる場合(ステートメント形式ラムダ)、中括弧 {} で処理を囲み、
戻り値がある場合は明示的に return キーワードを使って値を返す必要があります。

Func<int, int> addOneComplex = x =>
{
    Console.WriteLine($"入力値: {x}");
    return x + 1; // return が必要
};

ラムダ式内での変数の扱いと代入方法

ラムダ式は、
それが定義されたスコープ(外側のメソッドなど)にある変数(外部変数)を参照したり、
変更したりすることができます。
この仕組みをクロージャ (Closure) と呼びます。

int factor = 10; // ラムダ式の外側で定義された変数 (外部変数)
Func<int, int> multiplier = n => n * factor; // ラムダ式内で外部変数 factor を参照

Console.WriteLine(multiplier(5)); // 50 (5 * 10)

factor = 20; // 外部変数の値を変更

Console.WriteLine(multiplier(5)); // 100 (5 * 20) - 変更が反映される!

このように、ラムダ式は定義された時点の変数を「キャプチャ」し、
後で実行されるときもその変数にアクセスできます。
さらに、ラムダ式が実行されるたびに外部変数の最新の値が参照されます。

ラムダ式内で外部変数に値を代入することも可能です。

int counter = 0;
Action increment = () => counter++; // ラムダ式内で外部変数 counter を変更

increment();
increment();
Console.WriteLine(counter); // 2

List<Action> actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
    int temp = i; // ループごとに新しい変数を作成するのが重要
    actions.Add(() => Console.WriteLine(temp)); // temp をキャプチャ
}

foreach (var action in actions)
{
    action(); // 0, 1, 2 と出力される
}

// 注意: もし temp を使わずに直接 i をキャプチャすると...
List<Action> actionsWrong = new List<Action>();
for (int i = 0; i < 3; i++)
{
    // この i はループ全体で共有されるため、
    // ループ終了時の値 (3) がキャプチャされてしまう
    actionsWrong.Add(() => Console.WriteLine(i));
}
foreach (var action in actionsWrong)
{
    action(); // 3, 3, 3 と出力されてしまう!
}

特にループ内でラムダ式を使う場合、
ループ変数を直接キャプチャすると意図しない結果になることがあるため、
ループごとに一時変数にコピーしてからキャプチャするのが一般的なテクニックです。

複数行にわたるラムダ式の書き方と注意点

ラムダ式の処理内容が複雑で、
1行の式では表現できない場合は、
中括弧 {} を使って複数行のステートメント(文)を記述できます。
これをステートメント形式ラムダと呼びます。

Func<int, int, string> compareNumbers = (a, b) =>
{
    Console.WriteLine($"比較する数値: {a} と {b}");
    if (a > b)
    {
        return $"{a} は {b} より大きい";
    }
    else if (a < b)
    {
        return $"{a} は {b} より小さい";
    }
    else
    {
        return $"{a} と {b} は等しい";
    }
}; // セミコロンを忘れないように注意

Console.WriteLine(compareNumbers(10, 5));
Console.WriteLine(compareNumbers(3, 7));
Console.WriteLine(compareNumbers(4, 4));

注意点:

  • ステートメント形式ラムダでは、処理ブロックを {} で囲む必要があります。
  • 戻り値があるデリゲート型 (Func など) の場合、
    return 文を使って明示的に値を返す必要があります。
    式形式ラムダのように自動で最後の式の評価結果が返ることはありません。
  • 処理ブロックの最後にセミコロン ; が必要です
    (これはラムダ式を変数に代入する文の終わりを示すものです)。

複雑なロジックが必要な場合はステートメント形式ラムダが便利ですが、
あまりに長くなるようであれば、
通常のメソッドとして切り出すことを検討した方が可読性が高まる場合もあります

ラムダ式の中でif文を使う実践的な方法

ステートメント形式ラムダを使えば、
ラムダ式の中に if 文などの制御構文を含めることができます。
これは、特定の条件に基づいて処理を分岐させたい場合に役立ちます。

List<int> numbers = new List<int> { 1, -2, 3, -4, 5, 0 };

// 正の数だけを抽出する (LINQ の Where メソッドと if 文)
IEnumerable<int> positiveNumbers = numbers.Where(n =>
{
    // ここでは単純な例だが、もっと複雑な条件判定も可能
    if (n > 0)
    {
        return true; // 条件に合致する場合は true を返す
    }
    else
    {
        return false; // 条件に合致しない場合は false を返す
    }
});

// 上記は式形式ラムダでシンプルに書ける
// IEnumerable<int> positiveNumbers = numbers.Where(n => n > 0);

Console.WriteLine("正の数:");
foreach (int n in positiveNumbers)
{
    Console.Write($"{n} "); // 1 3 5
}
Console.WriteLine();

// 数値を文字列に変換する際、0 は "Zero"、それ以外は数値文字列にする
List<string> numberStrings = numbers.Select(n =>
{
    if (n == 0)
    {
        return "Zero";
    }
    else
    {
        return n.ToString(); // return が必要
    }
}).ToList();

Console.WriteLine("変換後の文字列:");
Console.WriteLine(string.Join(", ", numberStrings)); // 1, -2, 3, -4, 5, Zero

このように、LINQ の Where (フィルタリング) や Select (変換) メソッドなどで、
より複雑な条件判定や処理を行う際に、ラムダ式内の if 文が活用できます。

実践!C# ラムダ式の具体的な使いどころは?

ラムダ式は、C#プログラミングの様々な場面で活躍します。
ここでは、LINQ 以外での代表的な使いどころをいくつか紹介します。

1.LINQ (Language Integrated Query)

最もよく使われる場面です。
コレクションやデータソースに対する問い合わせ(フィルタリング、射影、ソートなど)を簡潔に記述できます。
(次のセクションで詳しく解説します)

2.イベントハンドラ

GUIアプリケーション(Windows Forms, WPF, Blazorなど)で、
ボタンクリックなどのイベントが発生した際の処理を記述するのに使われます。

// 例: ボタンクリックでメッセージを表示 (WPFやWindows Formsなど)
// button1.Click += new EventHandler(button1_Click); // 従来の書き方
// private void button1_Click(object sender, EventArgs e) { MessageBox.Show("クリックされました!"); }

// ラムダ式を使った書き方
button1.Click += (sender, e) =>
{
    MessageBox.Show("ラムダ式でクリックされました!");
};

3.リストのソート

List<T>.Sort() メソッドで、独自の比較ロジックを指定する際に使えます。

List<string> fruits = new List<string> { "Banana", "Apple", "Cherry" };

// 文字列の長さでソートする
fruits.Sort((x, y) => x.Length.CompareTo(y.Length));

// fruits は {"Apple", "Cherry", "Banana"} になる
Console.WriteLine(string.Join(", ", fruits));

4.非同期処理

Task.Run などを使って、
時間のかかる処理をバックグラウンドで実行させる場合に、
実行する処理内容をラムダ式で渡すことが多いです。

Console.WriteLine("非同期処理を開始します...");
Task task = Task.Run(() =>
{
    // 時間のかかる処理をシミュレート
    Thread.Sleep(2000);
    Console.WriteLine("非同期処理が完了しました。");
});
Console.WriteLine("メインスレッドは他の処理を続行できます。");
await task; // 非同期処理の完了を待つ (async メソッド内)

5.Predicate<T> デリゲート

条件に合致するかどうかを判定するメソッドを表すデリゲートで、List<T>.FindAll などで使われます。

List<int> scores = new List<int> { 80, 45, 95, 60, 72 };

// 70点以上のスコアを検索
List<int> highScores = scores.FindAll(score => score >= 70);

// highScores は { 80, 95, 72 } になる
Console.WriteLine("70点以上: " + string.Join(", ", highScores));

これらの例のように、
メソッドを引数として渡したい場面の多くで、ラムダ式がコードをシンプルかつ効率的にします。

LINQと組み合わせるラムダ式の威力とサンプルコード

ラムダ式が最もその威力を発揮するのが LINQ (Language Integrated Query) との組み合わせです。

LINQのメソッド構文では、
ほとんどの拡張メソッドが引数としてデリゲート(特に FuncAction)を受け取り、
その処理内容をラムダ式で記述するのが一般的です。

いくつか代表的なLINQメソッドとラムダ式の組み合わせ例を見てみましょう。

using System.Linq; // LINQ を使うには必須

// サンプルデータ
List<Product> products = new List<Product>
{
    new Product { Name = "Laptop", Category = "Electronics", Price = 120000 },
    new Product { Name = "Mouse", Category = "Electronics", Price = 3000 },
    new Product { Name = "Keyboard", Category = "Electronics", Price = 8000 },
    new Product { Name = "Desk Chair", Category = "Furniture", Price = 15000 },
    new Product { Name = "Book", Category = "Stationery", Price = 2000 }
};

// 1. Where: 条件に合う要素をフィルタリング (Electronicsカテゴリの商品)
var electronics = products.Where(p => p.Category == "Electronics");

Console.WriteLine("--- Electronics ---");
foreach (var p in electronics) Console.WriteLine($"{p.Name}: {p.Price}");

// 2. Select: 各要素を別の形に変換 (商品名だけを抽出)
var productNames = products.Select(p => p.Name);

Console.WriteLine("\n--- Product Names ---");
Console.WriteLine(string.Join(", ", productNames));

// 3. OrderBy / OrderByDescending: 要素を並び替える (価格昇順)
var sortedByPriceAsc = products.OrderBy(p => p.Price);

Console.WriteLine("\n--- Sorted by Price (Asc) ---");
foreach (var p in sortedByPriceAsc) Console.WriteLine($"{p.Name}: {p.Price}");

// 4. FirstOrDefault: 条件に合う最初の要素を取得 (価格が10000円以上の最初の商品)
// なければ null (参照型の場合) またはデフォルト値 (値型の場合) を返す
Product expensiveProduct = products.FirstOrDefault(p => p.Price >= 10000);

Console.WriteLine("\n--- First product >= 10000 ---");
if (expensiveProduct != null)
    Console.WriteLine($"{expensiveProduct.Name}: {expensiveProduct.Price}");
else
    Console.WriteLine("該当なし");

// 5. Count: 条件に合う要素の数を数える (価格が5000円未満の商品数)
int cheapProductCount = products.Count(p => p.Price < 5000);

Console.WriteLine($"\n--- Count < 5000 ---");
Console.WriteLine($"Number of products cheaper than 5000: {cheapProductCount}");

// 6. Any: 条件に合う要素が存在するかどうかを調べる (Furnitureカテゴリの商品があるか)
bool hasFurniture = products.Any(p => p.Category == "Furniture");

Console.WriteLine($"\n--- Any Furniture? ---");
Console.WriteLine($"Is there any furniture? {hasFurniture}");

// 7. メソッドチェーン: 複数の操作を繋げる (Electronicsカテゴリで価格が5000円以上の商品を価格降順で取得し、名前と価格を匿名型で選択)
var filteredSortedProducts = products
    .Where(p => p.Category == "Electronics" && p.Price >= 5000)
    .OrderByDescending(p => p.Price)
    .Select(p => new { ProductName = p.Name, ProductPrice = p.Price }); // 匿名型

Console.WriteLine("\n--- Electronics >= 5000, Sorted Desc, Selected ---");
foreach (var p in filteredSortedProducts) Console.WriteLine($"{p.ProductName}: {p.ProductPrice}");


// サンプル用クラス定義
public class Product
{
    public string Name { get; set; }
    public string Category { get; set; }
    public int Price { get; set; }
}

このように、
LINQメソッドとラムダ式を組み合わせることで、
複雑なデータ操作も非常に読みやすく、簡潔に記述できます。
これが、C#開発においてラムダ式の理解が重要視される大きな理由です。

C# ラムダ式をマスターして開発効率を向上させよう

この記事では、C#のラムダ式について、
その基本概念から書き方、デリゲートとの関係、
そしてLINQをはじめとする実践的な使い方まで、幅広く解説してきました。

ラムダ式は、
最初は少し奇妙に見えるかもしれませんが、
慣れてしまえばコードを劇的に簡潔にし、開発効率を大きく向上させる強力なツールです。
特にLINQを使ったデータ操作では、その恩恵を最大限に受けることができます。

今回学んだ内容をまとめると、以下のようになります。

  • ラムダ式は => を使った匿名関数である。
  • コードの簡潔化、可読性向上、LINQとの連携などのメリットがある。
  • 引数の数や戻り値の有無によって書き方が変わる (Action, Func)。
  • 外部変数をキャプチャするクロージャの仕組みを持つ。
  • {} を使って複数行の処理や if 文などの制御構文も記述できる。
  • LINQ、イベントハンドラ、非同期処理などで広く活用される。

C#のラムダ式は、現代的なC#プログラミングの基礎となる重要な機能です。
ぜひ、実際のコーディングで積極的にラムダ式を使ってみてください。
使えば使うほど、その便利さと強力さを実感できるはずです。頑張ってください!

他にもC#の様々なテクニックの紹介やエンジニアに役立つ情報を発信していますので興味のある方は以下のリンクからどうぞ!

C#やエンジニアに役立つ情報はこちら

安定して収入を上げたいエンジニアはこちら

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

<PR>

目次