C# ラムダ式とは?初心者がつまずく書き方・使い方・LINQでの活用をわかりやすく解説

はじめに

C#のラムダ式は、LINQやイベント処理、コレクション操作などで頻繁に登場する重要な書き方です。

しかし初心者にとっては、次のように感じやすい文法でもあります。

x => x > 10x はどこから来たの?」
=> の左側と右側は何を意味しているの?」
return を書くときと書かないときの違いがわからない」
「LINQと一緒に出てくると急に読みにくい」

ラムダ式は、最初は記号が多く見えて難しく感じますが、本質はとてもシンプルです。この記事では、C#のラムダ式とは何か、基本構文、LINQでの使い方、初心者がつまずきやすいポイント、よくあるエラー、読みやすく書くコツまで順番に解説します。

1. C#のラムダ式とは?初心者向けにひとことで解説

C#のラムダ式とは、ひとことで言うと「その場で書ける小さな関数」です。

たとえば、数値の一覧から偶数だけを取り出したい場合、次のように書けます。

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

var evenNumbers = numbers.Where(n => n % 2 == 0);

この中の n => n % 2 == 0 がラムダ式です。

n を受け取り、n % 2 == 0 という条件を判定する小さな処理を書いています。

1-1. ラムダ式は「その場で書ける小さな関数」

通常、処理を関数として書く場合は、次のようにメソッドを定義します。

C#
static bool IsEven(int n)
{
return n % 2 == 0;
}

そして、呼び出す側でそのメソッドを使います。

C#
var evenNumbers = numbers.Where(IsEven);

一方、ラムダ式を使うと、わざわざ別のメソッドを作らずに、その場で処理を書けます。

C#
var evenNumbers = numbers.Where(n => n % 2 == 0);

短い処理であれば、ラムダ式のほうが「何をしたいのか」が近い場所に書けるため、読みやすくなることがあります。

1-2. ラムダ式で使う「=>」ラムダ演算子の意味

ラムダ式で使う => は、ラムダ演算子と呼ばれます。

読み方としては、次のように考えるとわかりやすいです。

C#
n => n % 2 == 0

これは、

n を受け取って、n % 2 == 0 を返す

という意味です。

=> の左側には「入力」、右側には「処理」や「結果」を書きます。

C#
x => x * 2

これは、

x を受け取って、x * 2 を返す

という意味です。

1-3. 通常のメソッド・匿名メソッドとの違い

通常のメソッドは、名前を付けて定義します。

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

匿名メソッドは、名前を付けずに処理を書ける古い書き方です。

C#
Func<int, int> doubleFunc = delegate (int x)
{
return x * 2;
};

ラムダ式では、同じ処理をより短く書けます。

C#
Func<int, int> doubleFunc = x => x * 2;

ラムダ式は、匿名メソッドよりも簡潔で、LINQとの相性も良いため、現在のC#では非常によく使われます。

1-4. C#でラムダ式がよく使われる場面

C#のラムダ式は、主に次のような場面で使われます。

LINQでリストや配列を絞り込むとき。

C#
var adults = people.Where(person => person.Age >= 20);

データを変換するとき。

C#
var names = people.Select(person => person.Name);

並び替えをするとき。

C#
var sorted = people.OrderBy(person => person.Age);

イベント処理を書くとき。

C#
button.Click += (sender, e) =>
{
Console.WriteLine("ボタンがクリックされました");
};

非同期処理を書くとき。

C#
Func<Task> loadAsync = async () =>
{
await LoadDataAsync();
};

特にLINQを使う場面では、ラムダ式を読めることがC#コードを理解するうえで非常に重要です。

2. C#ラムダ式の基本構文と読み方

ラムダ式の基本構文は、とてもシンプルです。

C#
引数 => 処理

または、処理が複数行になる場合は次のように書きます。

C#
引数 =>
{
処理;
return 戻り値;
}

2-1. 基本形:引数 => 処理

もっとも基本的な形は、次のようなラムダ式です。

C#
x => x * 2

これは、x という値を受け取り、x * 2 を返す処理です。

Func<int, int> に代入すると、次のように使えます。

C#
Func<int, int> doubleNumber = x => x * 2;

Console.WriteLine(doubleNumber(5)); // 10

Func<int, int> は、「intを受け取り、intを返す処理」を表します。

2-2. 引数が1つの場合の書き方

引数が1つの場合、型を省略して次のように書けます。

C#
x => x + 1

型を明示する場合は、引数を丸かっこで囲みます。

C#
(int x) => x + 1

LINQでは、型推論が働くため、次のように型を省略することがほとんどです。

C#
var result = numbers.Where(n => n > 10);

この n は、numbers の各要素を表しています。

2-3. 引数が複数ある場合の書き方

引数が複数ある場合は、丸かっこが必要です。

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

たとえば、2つのintを受け取って合計を返す処理は次のように書けます。

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

Console.WriteLine(add(3, 5)); // 8

型を明示する場合は、次のように書きます。

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

2-4. 引数がない場合の書き方

引数がないラムダ式では、空の丸かっこ () を書きます。

C#
() => Console.WriteLine("Hello")

たとえば、引数なしで現在時刻を返す処理は次のように書けます。

C#
Func<DateTime> getNow = () => DateTime.Now;

Console.WriteLine(getNow());

処理を実行するだけで戻り値がない場合は、Action を使います。

C#
Action greet = () => Console.WriteLine("こんにちは");

greet();

2-5. 戻り値があるラムダ式とないラムダ式

ラムダ式には、戻り値があるものとないものがあります。

戻り値があるラムダ式の例です。

C#
Func<int, int> square = x => x * x;

Console.WriteLine(square(4)); // 16

このラムダ式は、x * x の結果を返します。

戻り値がないラムダ式の例です。

C#
Action<string> printMessage = message => Console.WriteLine(message);

printMessage("Hello");

このラムダ式は、画面に文字列を表示するだけで値を返しません。

戻り値がある場合は Func、戻り値がない場合は Action と考えると理解しやすくなります。

2-6. 型推論で型を省略できる理由

ラムダ式では、引数の型を省略できることがよくあります。

C#
var result = numbers.Where(n => n > 10);

このとき、n の型は int です。

なぜなら、numbersList<int> であり、Where はその要素を1つずつ取り出して条件判定するメソッドだからです。

つまり、C#コンパイラは周囲の情報から「n はintだな」と判断できます。これを型推論と呼びます。

一方で、周囲の情報が足りない場合は、型を推論できずエラーになります。

C#
// エラーになりやすい例
var func = x => x * 2;

この場合、x がintなのかdoubleなのか判断できません。

次のように型を明示すれば解決できます。

C#
Func<int, int> func = x => x * 2;

3. ラムダ式の書き方パターンをコード例で理解する

ラムダ式には、大きく分けて「式形式」と「ステートメント形式」があります。

短い処理なら式形式、複数行の処理ならステートメント形式を使います。

3-1. 1行で書く式形式のラムダ式

式形式のラムダ式は、1つの式だけを書くシンプルな形です。

C#
x => x * 2

LINQでもよく使われます。

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

var doubled = numbers.Select(n => n * 2);

この例では、各数値を2倍に変換しています。

式形式のラムダ式では、return を書きません。右側の式の結果がそのまま戻り値になります。

3-2. 複数行で書くステートメント形式のラムダ式

処理が複数行になる場合は、波かっこ {} を使います。

C#
Func<int, int> calculate = x =>
{
int result = x * 2;
return result;
};

このように、変数を定義したり、複数の処理を書いたりできます。

LINQでも使えます。

C#
var result = numbers.Select(n =>
{
int doubled = n * 2;
return doubled;
});

ただし、LINQの中に複雑な処理を書きすぎると読みにくくなるため、必要に応じてメソッドに切り出すことも大切です。

3-3. returnが必要なケース・不要なケース

式形式のラムダ式では、return は不要です。

C#
Func<int, int> doubleNumber = x => x * 2;

これは、x * 2 の結果がそのまま返されます。

一方、波かっこ {} を使うステートメント形式では、戻り値がある場合に return が必要です。

C#
Func<int, int> doubleNumber = x =>
{
return x * 2;
};

次のように、波かっこを使っているのに return を書かないとエラーになります。

C#
// エラー
Func<int, int> doubleNumber = x =>
{
x * 2;
};

戻り値がない Action の場合は、return は不要です。

C#
Action<string> print = message =>
{
Console.WriteLine(message);
};

3-4. 型を明示する書き方

ラムダ式の引数は、型を明示して書くこともできます。

C#
Func<int, int> doubleNumber = (int x) => x * 2;

引数が複数ある場合も同じです。

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

ただし、型を明示する場合は、引数が1つでも丸かっこが必要です。

C#
// OK
Func<int, int> func = (int x) => x + 1;

// NG
// Func<int, int> func = int x => x + 1;

普段は型推論に任せて省略することが多いですが、エラーが出る場合や読みやすさを優先したい場合は型を明示するとよいでしょう。

3-5. よくある省略形と読みやすい書き方

ラムダ式では、次のような省略がよく使われます。

C#
n => n > 0

これは本来、次のように型を書いても同じ意味です。

C#
(int n) => n > 0

さらに、ステートメント形式で書くと次のようになります。

C#
(int n) =>
{
return n > 0;
}

短い条件であれば、最初の書き方が読みやすいです。

C#
var positives = numbers.Where(n => n > 0);

一方で、条件が長くなる場合は、無理に1行で書かないほうがよいです。

C#
var result = users.Where(user =>
{
bool isAdult = user.Age >= 20;
bool isActive = user.IsActive;

return isAdult && isActive;
});

さらに複雑な場合は、メソッドに切り出すと読みやすくなります。

C#
var result = users.Where(IsTargetUser);

static bool IsTargetUser(User user)
{
return user.Age >= 20 && user.IsActive;
}

4. ラムダ式を理解するための前提知識

ラムダ式をしっかり理解するには、デリゲート、Func、Action、Predicateの考え方を知っておくと役立ちます。

ラムダ式は単独で存在するというより、「何らかの処理を表す型」に代入されたり、メソッドの引数として渡されたりします。

4-1. デリゲートとは何か

デリゲートとは、メソッドを変数のように扱うための型です。

たとえば、次のようにデリゲートを定義できます。

C#
delegate int Calculator(int x, int y);

これは、「intを2つ受け取り、intを返すメソッド」を入れられる型です。

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

Calculator calc = Add;

Console.WriteLine(calc(3, 5)); // 8

デリゲートを使うと、処理そのものを変数に入れたり、別のメソッドに渡したりできます。

ラムダ式は、このデリゲートに代入できます。

C#
Calculator calc = (x, y) => x + y;

4-2. Funcとは何か

Func は、戻り値がある処理を表すために用意されている汎用的なデリゲートです。

C#
Func<int, int> doubleNumber = x => x * 2;

Func<int, int> は、「intを受け取り、intを返す処理」を表します。

複数の引数を持つ場合は、最後の型が戻り値になります。

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

この場合、最初の int と2番目の int が引数、最後の int が戻り値です。

つまり、次の意味になります。

int, int を受け取り、int を返す

4-3. Actionとは何か

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

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

これは、「stringを受け取り、戻り値なしで処理する」という意味です。

引数がない場合は、次のように書きます。

C#
Action greet = () => Console.WriteLine("こんにちは");

Action は、ログ出力、画面表示、イベント処理など、「何かを実行するだけ」の処理でよく使われます。

4-4. Predicateとは何か

Predicate<T> は、ある値が条件を満たすかどうかを判定するためのデリゲートです。

C#
Predicate<int> isEven = n => n % 2 == 0;

Console.WriteLine(isEven(4)); // True

Predicate<int> は、「intを受け取り、boolを返す処理」を表します。

つまり、次の Func<int, bool> と似た意味です。

C#
Func<int, bool> isEven = n => n % 2 == 0;

条件判定を表す場合は、Predicate<T> を使うと意図が伝わりやすいことがあります。

4-5. ラムダ式とデリゲートの関係

ラムダ式は、デリゲート型に変換されて使われます。

C#
Func<int, int> doubleNumber = x => x * 2;

この例では、x => x * 2 というラムダ式が、Func<int, int> というデリゲート型に代入されています。

LINQの Where も、条件を表す処理を引数として受け取ります。

C#
var result = numbers.Where(n => n > 10);

ここで渡している n => n > 10 は、「数値を1つ受け取ってboolを返す処理」です。

つまり、ラムダ式は「処理を値として渡すための書き方」と考えると理解しやすくなります。

5. LINQで使うラムダ式の基本

C#のラムダ式を学ぶうえで、LINQは避けて通れません。

LINQでは、配列やリストなどのデータに対して、絞り込み、変換、並び替え、集計などを簡潔に書けます。

5-1. LINQとは?ラムダ式と一緒に使われる理由

LINQは、コレクションやデータソースに対して問い合わせを行うための機能です。

たとえば、リストから条件に合うデータだけを取り出す処理を、for文で書くと次のようになります。

C#
var result = new List<int>();

foreach (var n in numbers)
{
if (n > 10)
{
result.Add(n);
}
}

LINQとラムダ式を使うと、次のように書けます。

C#
var result = numbers.Where(n => n > 10);

「numbersの中から、nが10より大きいものを取り出す」と読めます。

LINQは、ラムダ式を使うことで「何をしたいか」を短く表現できます。

5-2. Whereで条件に合うデータを絞り込む

Where は、条件に合う要素だけを取り出すメソッドです。

C#
var numbers = new List<int> { 5, 10, 15, 20 };

var result = numbers.Where(n => n >= 10);

この場合、10, 15, 20 が取り出されます。

n => n >= 10 は、「nが10以上かどうか」を判定する条件です。

オブジェクトのリストでも使えます。

C#
var adults = users.Where(user => user.Age >= 20);

5-3. Selectで必要な形に変換する

Select は、要素を別の形に変換するメソッドです。

C#
var numbers = new List<int> { 1, 2, 3 };

var doubled = numbers.Select(n => n * 2);

この場合、2, 4, 6 に変換されます。

オブジェクトから特定のプロパティだけを取り出すときにもよく使います。

C#
var names = users.Select(user => user.Name);

これは、ユーザー一覧から名前だけを取り出す処理です。

5-4. OrderByで並び替える

OrderBy は、指定した値を基準に昇順で並び替えるメソッドです。

C#
var sorted = users.OrderBy(user => user.Age);

これは、ユーザーを年齢の小さい順に並び替えます。

降順にしたい場合は、OrderByDescending を使います。

C#
var sorted = users.OrderByDescending(user => user.Age);

複数条件で並び替える場合は、ThenBy を使います。

C#
var sorted = users
.OrderBy(user => user.Age)
.ThenBy(user => user.Name);

5-5. Any・All・FirstOrDefaultで条件判定する

Any は、条件に合う要素が1つでもあるかを判定します。

C#
bool exists = users.Any(user => user.Age >= 20);

All は、すべての要素が条件を満たすかを判定します。

C#
bool allAdults = users.All(user => user.Age >= 20);

FirstOrDefault は、条件に合う最初の要素を取得します。見つからない場合は既定値を返します。

C#
var user = users.FirstOrDefault(user => user.Name == "田中");

FirstOrDefault の結果は null になる可能性があるため、参照型の場合はnullチェックを忘れないようにしましょう。

C#
if (user != null)
{
Console.WriteLine(user.Name);
}

5-6. Count・Sum・Max・Minで集計する

LINQでは、ラムダ式を使って集計もできます。

条件に合う件数を数える場合です。

C#
int adultCount = users.Count(user => user.Age >= 20);

合計を求める場合です。

C#
int totalAge = users.Sum(user => user.Age);

最大値を求める場合です。

C#
int maxAge = users.Max(user => user.Age);

最小値を求める場合です。

C#
int minAge = users.Min(user => user.Age);

このように、ラムダ式を使うと「何を基準に集計するか」を簡潔に書けます。

6. LINQでよく使うラムダ式の実践例

ここからは、実際によく使うラムダ式の例を見ていきます。

サンプルとして、次のような User クラスを使います。

C#
public class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
public bool IsActive { get; set; }
}

6-1. Listから条件に合う要素だけ取り出す

数値リストから10以上の要素だけを取り出します。

C#
var numbers = new List<int> { 3, 10, 15, 8, 20 };

var result = numbers.Where(n => n >= 10).ToList();

Where の中の n は、リスト内の各要素です。

処理のイメージは次のようになります。

3  は 10以上? false
10 は 10以上? true
15 は 10以上? true
8 は 10以上? false
20 は 10以上? true

結果は 10, 15, 20 になります。

6-2. オブジェクトのプロパティで絞り込む

ユーザー一覧から20歳以上のユーザーだけを取り出します。

C#
var adults = users.Where(user => user.Age >= 20).ToList();

user => user.Age >= 20 は、「userのAgeが20以上か」を判定しています。

特定の名前で絞り込むこともできます。

C#
var tanakaList = users.Where(user => user.Name == "田中").ToList();

オブジェクトのリストでは、ラムダ式の引数名を x ではなく user のように意味のある名前にすると読みやすくなります。

6-3. 複数条件を組み合わせる

複数の条件を組み合わせる場合は、&&|| を使います。

20歳以上かつ有効なユーザーを取り出す例です。

C#
var result = users
.Where(user => user.Age >= 20 && user.IsActive)
.ToList();

どちらか一方の条件を満たせばよい場合は、|| を使います。

C#
var result = users
.Where(user => user.Age < 20 || !user.IsActive)
.ToList();

条件が長くなる場合は、改行すると読みやすくなります。

C#
var result = users
.Where(user =>
user.Age >= 20 &&
user.IsActive &&
user.Name.StartsWith("田"))
.ToList();

6-4. 文字列検索にラムダ式を使う

文字列の検索では、ContainsStartsWithEndsWith などをよく使います。

名前に「田」が含まれるユーザーを取得する例です。

C#
var result = users
.Where(user => user.Name.Contains("田"))
.ToList();

名前が「田」で始まるユーザーを取得する例です。

C#
var result = users
.Where(user => user.Name.StartsWith("田"))
.ToList();

名前が「子」で終わるユーザーを取得する例です。

C#
var result = users
.Where(user => user.Name.EndsWith("子"))
.ToList();

文字列検索では、大文字小文字の違いや全角半角の違いに注意が必要です。

6-5. nullを考慮した安全な書き方

プロパティがnullになる可能性がある場合、次のようなコードはエラーになることがあります。

C#
var result = users.Where(user => user.Name.Contains("田"));

Name がnullの場合、Contains を呼び出せないためです。

安全に書くには、null条件演算子 ?. を使います。

C#
var result = users
.Where(user => user.Name?.Contains("田") == true)
.ToList();

または、先にnullでないことを確認します。

C#
var result = users
.Where(user => user.Name != null && user.Name.Contains("田"))
.ToList();

FirstOrDefault を使う場合も、結果がnullになる可能性があります。

C#
var user = users.FirstOrDefault(user => user.Age >= 20);

Console.WriteLine(user?.Name);

?. を使うことで、user がnullでも例外を防げます。

6-6. メソッドチェーンで複数の処理をつなげる

LINQでは、複数の処理をつなげて書けます。

C#
var names = users
.Where(user => user.Age >= 20)
.OrderBy(user => user.Name)
.Select(user => user.Name)
.ToList();

このコードは、次の順番で処理しています。

20歳以上のユーザーに絞り込む
名前順に並び替える
名前だけを取り出す
リストに変換する

メソッドチェーンが長くなる場合は、1行で書かずに改行すると読みやすくなります。

C#
var result = users
.Where(user => user.IsActive)
.Where(user => user.Age >= 20)
.OrderBy(user => user.Age)
.Select(user => new
{
user.Name,
user.Age
})
.ToList();

7. 初心者がラムダ式でつまずきやすいポイント

ラムダ式で初心者がつまずく原因の多くは、「記号の意味」と「変数の正体」がわかりにくいことです。

ここでは、よくある疑問を整理します。

7-1. 「x」や「n」は何を表しているのか

ラムダ式でよく見る xn は、単なる引数名です。

C#
numbers.Where(n => n > 10)

この n は、numbers の中から1つずつ取り出される要素を表しています。

つまり、次のforeach文の n と同じような役割です。

C#
foreach (var n in numbers)
{
if (n > 10)
{
// 条件に合う
}
}

名前は自由に変えられます。

C#
numbers.Where(number => number > 10)

初心者のうちは、n よりも numberuserproduct のように意味が伝わる名前を使うと読みやすくなります。

7-2. => の左側と右側の役割がわからない

=> の左側は入力、右側は処理です。

C#
user => user.Age >= 20

これは、

userを受け取って、user.Age >= 20を判定する

という意味です。

Select の場合も同じです。

C#
user => user.Name

これは、

userを受け取って、user.Nameを返す

という意味です。

Where では右側が条件、Select では右側が変換結果になると考えると理解しやすいです。

7-3. 引数の型をいつ省略できるのかわからない

引数の型を省略できるのは、コンパイラが型を推論できる場合です。

C#
var result = numbers.Where(n => n > 10);

numbersList<int> であれば、n はintだと判断できます。

一方で、次のようにラムダ式だけを書いても、型がわかりません。

C#
// エラー
var func = x => x * 2;

この場合は、代入先の型を明示します。

C#
Func<int, int> func = x => x * 2;

ラムダ式は、文脈があって初めて型が決まると覚えておきましょう。

7-4. returnを書くべきか迷う

return が必要かどうかは、波かっこ {} を使っているかで判断するとわかりやすいです。

式形式では return は不要です。

C#
Func<int, int> func = x => x * 2;

ステートメント形式で戻り値がある場合は return が必要です。

C#
Func<int, int> func = x =>
{
return x * 2;
};

戻り値がない場合は return は不要です。

C#
Action<string> print = message =>
{
Console.WriteLine(message);
};

7-5. 波かっこ{}を使う場合と使わない場合の違い

波かっこを使わない場合は、右側に1つの式を書きます。

C#
n => n > 10

波かっこを使う場合は、複数の文を書けます。

C#
n =>
{
Console.WriteLine(n);
return n > 10;
}

ただし、波かっこを使った場合、戻り値が必要なら return を書かなければなりません。

短い条件や変換なら波かっこなし、複数行の処理なら波かっこあり、と考えるとよいでしょう。

7-6. LINQの処理順が読みにくい

LINQのメソッドチェーンは、慣れないうちは読みにくく感じます。

C#
var result = users
.Where(user => user.IsActive)
.OrderBy(user => user.Age)
.Select(user => user.Name)
.ToList();

この場合は、上から順番に読みます。

usersから
有効なユーザーに絞り込み
年齢順に並び替え
名前だけを取り出し
リストにする

ポイントは、ラムダ式だけを見るのではなく、WhereOrderBySelect などのメソッド名とセットで読むことです。

8. ラムダ式でよくあるエラーと解決方法

ラムダ式のエラーは、型、戻り値、null、LINQの仕様に関係するものが多いです。

エラーメッセージが難しく見えても、確認するポイントを絞れば解決しやすくなります。

8-1. 型を推論できないエラー

次のコードはエラーになります。

C#
var func = x => x * 2;

C#コンパイラは、x の型と戻り値の型を判断できません。

解決するには、代入先の型を明示します。

C#
Func<int, int> func = x => x * 2;

または、引数の型を明示します。

C#
Func<int, int> func = (int x) => x * 2;

ラムダ式は、どのデリゲート型に変換するのかが重要です。

8-2. 戻り値の型が合わないエラー

戻り値の型が代入先と合っていない場合もエラーになります。

C#
// エラー
Func<int, int> func = x => x > 0;

Func<int, int> はintを返す必要がありますが、x > 0 はboolを返します。

boolを返したい場合は、次のように書きます。

C#
Func<int, bool> func = x => x > 0;

または、intを返す処理に変更します。

C#
Func<int, int> func = x => x * 2;

8-3. returnの書き忘れ・書きすぎ

ステートメント形式で戻り値がある場合、return が必要です。

C#
// エラー
Func<int, int> func = x =>
{
x * 2;
};

正しくは次のように書きます。

C#
Func<int, int> func = x =>
{
return x * 2;
};

反対に、式形式では return を書けません。

C#
// エラー
Func<int, int> func = x => return x * 2;

正しくは次のように書きます。

C#
Func<int, int> func = x => x * 2;

8-4. null参照によるエラー

ラムダ式の中でnullの可能性がある値にアクセスすると、実行時エラーになることがあります。

C#
var result = users.Where(user => user.Name.Contains("田"));

Name がnullの場合、Contains が呼び出せません。

解決策は、nullチェックを入れることです。

C#
var result = users
.Where(user => user.Name != null && user.Name.Contains("田"))
.ToList();

または、null条件演算子を使います。

C#
var result = users
.Where(user => user.Name?.Contains("田") == true)
.ToList();

FirstOrDefault の結果もnullの可能性があります。

C#
var user = users.FirstOrDefault(user => user.Age >= 20);

Console.WriteLine(user?.Name);

8-5. LINQで想定と違う結果になる原因

LINQで想定と違う結果になる原因として多いのは、遅延実行です。

たとえば、次のコードを見てください。

C#
var numbers = new List<int> { 1, 2, 3 };

var query = numbers.Where(n => n > 1);

numbers.Add(4);

foreach (var n in query)
{
Console.WriteLine(n);
}

この場合、2, 3, 4 が出力されます。

Where を書いた時点では処理が実行されず、foreach で列挙されたときに実行されるためです。

その時点の結果を確定したい場合は、ToList() を使います。

C#
var query = numbers.Where(n => n > 1).ToList();

LINQでは、「いつ実行されるか」を意識することが大切です。

8-6. コンパイルエラーを読むときの確認ポイント

ラムダ式でエラーが出たら、次の点を確認しましょう。

まず、引数の型が推論できているかを確認します。

C#
Func<int, int> func = x => x * 2;

次に、戻り値の型が合っているかを確認します。

C#
Func<int, bool> isAdult = age => age >= 20;

波かっこを使っている場合は、return が必要か確認します。

C#
Func<int, int> func = x =>
{
return x * 2;
};

LINQの場合は、Where ならboolを返しているか、Select なら変換後の値を返しているかを確認しましょう。

C#
users.Where(user => user.Age >= 20); // boolを返す
users.Select(user => user.Name); // 値を返す

9. ラムダ式を読みやすく書くコツ

ラムダ式は便利ですが、短く書けるからといって常に読みやすくなるわけではありません。

読みやすいC#コードを書くためには、適切な長さ、名前、分割が大切です。

9-1. 引数名は意味が伝わる名前にする

短い例では、xn でも問題ありません。

C#
numbers.Where(n => n > 10);

しかし、オブジェクトを扱う場合は意味のある名前にしましょう。

C#
users.Where(user => user.Age >= 20);

次のように x だけだと、何を表しているのかわかりにくくなります。

C#
users.Where(x => x.Age >= 20);

小さなコードでは問題なくても、実務コードでは userorderproduct など、役割が伝わる名前を使うほうが読みやすくなります。

9-2. 複雑な処理は無理に1行で書かない

ラムダ式は1行で書けることが魅力ですが、長すぎると読みにくくなります。

C#
var result = users.Where(user => user.Age >= 20 && user.IsActive && user.Name.StartsWith("田") && user.CreatedAt.Year == DateTime.Now.Year);

このような場合は、改行したほうが読みやすいです。

C#
var result = users
.Where(user =>
user.Age >= 20 &&
user.IsActive &&
user.Name.StartsWith("田") &&
user.CreatedAt.Year == DateTime.Now.Year);

さらに複雑なら、メソッドに切り出しましょう。

9-3. 条件が長い場合はメソッドに切り出す

条件が長くなった場合、ラムダ式の中にすべて書くよりも、名前付きメソッドに切り出すと読みやすくなります。

C#
var result = users.Where(IsTargetUser);
C#
static bool IsTargetUser(User user)
{
return user.Age >= 20 &&
user.IsActive &&
user.Name.StartsWith("田");
}

このようにすると、呼び出し側では「対象ユーザーを絞り込んでいる」とすぐにわかります。

ラムダ式は便利ですが、処理の意味を名前で表現できる場合は、メソッドにしたほうがよいこともあります。

9-4. LINQを使いすぎない判断基準

LINQは便利ですが、すべてをLINQで書く必要はありません。

次のような場合は、for文やforeach文のほうが読みやすいことがあります。

複雑な分岐が多い場合。

C#
foreach (var user in users)
{
if (!user.IsActive)
{
continue;
}

if (user.Age < 20)
{
continue;
}

Console.WriteLine(user.Name);
}

途中で処理を中断したい場合や、複数の副作用を伴う場合も、LINQより通常のループのほうが適していることがあります。

LINQは「データを絞り込む」「変換する」「集計する」といった処理に向いています。一方で、複雑な業務処理を無理に詰め込むと読みにくくなります。

9-5. チーム開発で読みやすいラムダ式の書き方

チーム開発では、自分だけでなく他の人が読むことを意識する必要があります。

読みやすくするためには、次の点を意識しましょう。

短い処理はラムダ式で簡潔に書く。

C#
var adults = users.Where(user => user.Age >= 20);

長い条件は改行する。

C#
var result = users
.Where(user =>
user.Age >= 20 &&
user.IsActive);

さらに複雑な処理はメソッドに切り出す。

C#
var result = users.Where(IsActiveAdult);

ラムダ式は「短く書くため」だけではなく、「意図を近い場所に書くため」に使うと考えると、読みやすいコードになります。

10. ラムダ式の応用知識

基本的なラムダ式に慣れてきたら、クロージャ、asyncラムダ式、イベント処理、Expression Treeなども理解しておくと実務で役立ちます。

10-1. 外側の変数を使うクロージャ

ラムダ式の中では、外側で定義された変数を使えます。

C#
int threshold = 10;

var result = numbers.Where(n => n > threshold);

このように、ラムダ式が外側の変数を参照する仕組みをクロージャと呼びます。

便利な機能ですが、変数の値が後から変わる場合には注意が必要です。

C#
int threshold = 10;

Func<int, bool> isGreater = n => n > threshold;

threshold = 20;

Console.WriteLine(isGreater(15)); // False

ラムダ式は threshold の値そのものではなく、変数を参照しています。そのため、後から値が変わると結果も変わります。

10-2. 非同期処理で使うasyncラムダ式

ラムダ式は、非同期処理でも使えます。

C#
Func<Task> loadAsync = async () =>
{
await LoadDataAsync();
};

引数がある場合も同じです。

C#
Func<int, Task<string>> getNameAsync = async id =>
{
var user = await GetUserAsync(id);
return user.Name;
};

イベント処理でもasyncラムダ式を使うことがあります。

C#
button.Click += async (sender, e) =>
{
await SaveAsync();
};

ただし、asyncラムダ式では例外処理や戻り値の型に注意が必要です。async void になるケースは、主にイベントハンドラに限定して使うのが一般的です。

10-3. イベント処理で使うラムダ式

イベント処理では、ラムダ式を使ってその場で処理を書くことができます。

C#
button.Click += (sender, e) =>
{
Console.WriteLine("クリックされました");
};

短いイベント処理であれば、ラムダ式は便利です。

ただし、後からイベント解除をしたい場合は注意が必要です。

C#
button.Click += (sender, e) =>
{
Console.WriteLine("クリックされました");
};

このように直接ラムダ式を書くと、同じ処理を指定して解除することが難しくなります。

解除が必要な場合は、メソッドとして定義するか、デリゲートを変数に保持します。

C#
EventHandler handler = (sender, e) =>
{
Console.WriteLine("クリックされました");
};

button.Click += handler;
button.Click -= handler;

10-4. Expression Treeとは何か

ラムダ式は、デリゲートだけでなく、Expression Treeに変換されることもあります。

C#
Expression<Func<User, bool>> expression = user => user.Age >= 20;

Func<User, bool> は実行できる処理そのものを表します。

一方、Expression<Func<User, bool>> は、処理の構造をデータとして表します。

Entity Frameworkなどでは、このExpression Treeを解析してSQLに変換します。

C#
var adults = dbContext.Users.Where(user => user.Age >= 20);

このようなコードでは、ラムダ式がそのままメモリ上で実行されるのではなく、SQLに変換されてデータベース側で実行されることがあります。

10-5. Entity Frameworkでラムダ式を使うときの注意点

Entity Frameworkでラムダ式を使う場合、すべてのC#コードがSQLに変換できるわけではありません。

たとえば、独自メソッドをWhereの中で使うと、SQLに変換できずエラーになることがあります。

C#
var result = dbContext.Users
.Where(user => IsAdult(user))
.ToList();
C#
static bool IsAdult(User user)
{
return user.Age >= 20;
}

このようなメソッドは、データベース側でSQLとして実行できない場合があります。

Entity Frameworkでは、できるだけSQLに変換しやすい形で書くことが大切です。

C#
var result = dbContext.Users
.Where(user => user.Age >= 20)
.ToList();

また、ToList() を呼び出すタイミングにも注意しましょう。ToList() を呼ぶと、その時点でクエリが実行され、以降の処理はメモリ上で行われます。

11. ラムダ式と似た機能の違い

C#には、ラムダ式と似た機能がいくつかあります。

匿名メソッド、ローカル関数、メソッドグループ、クエリ構文などです。それぞれの違いを理解すると、状況に応じて適切な書き方を選べるようになります。

11-1. ラムダ式と匿名メソッドの違い

匿名メソッドは、名前のないメソッドを定義する古い書き方です。

C#
Func<int, int> doubleNumber = delegate (int x)
{
return x * 2;
};

ラムダ式では、同じ処理をより短く書けます。

C#
Func<int, int> doubleNumber = x => x * 2;

現在のC#では、ほとんどの場合ラムダ式が使われます。

匿名メソッドを新しく書く場面は少ないですが、古いコードを読むと登場することがあります。

11-2. ラムダ式とローカル関数の違い

ローカル関数は、メソッドの中に定義できる名前付きの関数です。

C#
void PrintAdults(List<User> users)
{
bool IsAdult(User user)
{
return user.Age >= 20;
}

var adults = users.Where(IsAdult);
}

ラムダ式でも同じようなことはできます。

C#
void PrintAdults(List<User> users)
{
Func<User, bool> isAdult = user => user.Age >= 20;

var adults = users.Where(isAdult);
}

短い処理をその場で渡すならラムダ式が便利です。

一方、処理に名前を付けたい場合、再帰を使いたい場合、複数箇所で使いたい場合は、ローカル関数のほうが読みやすいことがあります。

11-3. ラムダ式とメソッドグループの違い

メソッドグループとは、メソッド名だけを渡す書き方です。

C#
var result = numbers.Where(IsEven);
C#
static bool IsEven(int n)
{
return n % 2 == 0;
}

ラムダ式で書くと次のようになります。

C#
var result = numbers.Where(n => IsEven(n));

この場合、単にメソッドを呼ぶだけなら、メソッドグループのほうが簡潔です。

C#
var result = numbers.Where(IsEven);

条件がその場で完結するならラムダ式、すでにメソッドがあるならメソッドグループを使うとよいでしょう。

11-4. ラムダ式とクエリ構文の違い

LINQには、メソッド構文とクエリ構文があります。

ラムダ式を使うメソッド構文です。

C#
var result = users
.Where(user => user.Age >= 20)
.Select(user => user.Name);

SQLに似たクエリ構文です。

C#
var result =
from user in users
where user.Age >= 20
select user.Name;

どちらも同じような処理を表せます。

単純な絞り込みや変換では、メソッド構文がよく使われます。複数のfromやjoinを使う場合は、クエリ構文のほうが読みやすいこともあります。

11-5. どの書き方を選ぶべきか

基本的には、次のように考えるとよいです。

短い条件や変換をその場で書くならラムダ式。

C#
users.Where(user => user.Age >= 20);

すでに意味のあるメソッドがあるならメソッドグループ。

C#
users.Where(IsAdult);

複雑な処理に名前を付けたいならローカル関数や通常のメソッド。

C#
users.Where(IsTargetUser);

SQLのように読みたい場合やjoinが多い場合はクエリ構文。

C#
var result =
from user in users
where user.Age >= 20
select user.Name;

大切なのは、短さだけでなく、読みやすさと意図の伝わりやすさを基準に選ぶことです。

12. C#ラムダ式の学習ステップ

ラムダ式は、一度にすべてを覚えようとすると難しく感じます。

順番に学習すれば、自然に読めるようになります。

12-1. まずは基本構文を読めるようにする

最初に覚えるべき形はこれです。

C#
引数 => 処理

たとえば、次のコードを読めるようにしましょう。

C#
n => n > 10

これは、「nを受け取って、nが10より大きいか判定する」という意味です。

C#
user => user.Name

これは、「userを受け取って、userのNameを返す」という意味です。

まずは、=> の左側が入力、右側が処理だと理解することが大切です。

12-2. Func・Actionで短い例を書く

次に、FuncAction を使って短いラムダ式を書いてみましょう。

C#
Func<int, int> doubleNumber = x => x * 2;

Console.WriteLine(doubleNumber(5));

戻り値がない処理は Action で書きます。

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

print("Hello");

Func は戻り値あり、Action は戻り値なしと覚えるとわかりやすいです。

12-3. LINQのWhereとSelectで練習する

ラムダ式に慣れるには、LINQの WhereSelect を練習するのが効果的です。

Where は条件で絞り込みます。

C#
var result = numbers.Where(n => n > 10);

Select は別の形に変換します。

C#
var result = numbers.Select(n => n * 2);

オブジェクトでも練習しましょう。

C#
var adults = users.Where(user => user.Age >= 20);
var names = users.Select(user => user.Name);

Where はboolを返す、Select は変換後の値を返す、と意識すると理解しやすくなります。

12-4. 複数条件・並び替え・集計に進む

基本に慣れたら、複数条件や並び替え、集計に進みます。

C#
var result = users
.Where(user => user.Age >= 20 && user.IsActive)
.OrderBy(user => user.Name)
.ToList();

集計も試してみましょう。

C#
int count = users.Count(user => user.Age >= 20);
int totalAge = users.Sum(user => user.Age);

複数のLINQメソッドを組み合わせると、実務でよく見るコードに近づきます。

12-5. 実務コードを読んでパターンを覚える

ラムダ式は、書くだけでなく読む練習も重要です。

実務コードでは、次のようなパターンがよく出てきます。

C#
var activeUsers = users.Where(user => user.IsActive);
C#
var userNames = users.Select(user => user.Name);
C#
var exists = users.Any(user => user.Email == email);
C#
var user = users.FirstOrDefault(user => user.Id == id);

これらの形を何度も読むことで、ラムダ式に慣れていきます。

最初は難しく感じても、WhereSelectAnyFirstOrDefault のような頻出パターンから覚えれば十分です。

13. C#ラムダ式に関するよくある質問

最後に、C#のラムダ式について初心者が疑問に感じやすい点をまとめます。

13-1. ラムダ式は必ず使わないといけない?

ラムダ式は必ず使わなければならないものではありません。

通常のメソッドやfor文、foreach文でも同じ処理は書けます。

C#
foreach (var user in users)
{
if (user.Age >= 20)
{
Console.WriteLine(user.Name);
}
}

ただし、LINQやイベント処理などではラムダ式がよく使われるため、読めるようになっておくとC#のコード理解がかなり楽になります。

13-2. 初心者はどこまで覚えればいい?

初心者のうちは、まず次の内容を覚えれば十分です。

x => x * 2 のような基本構文を読めること。

Where で条件を指定できること。

C#
numbers.Where(n => n > 10);

Select で値を変換できること。

C#
numbers.Select(n => n * 2);

Func は戻り値あり、Action は戻り値なしだと理解すること。

最初からExpression Treeやクロージャの細かい仕様まで覚える必要はありません。まずはLINQでよく出るラムダ式を読めるようにしましょう。

13-3. ラムダ式は処理速度に影響する?

ラムダ式そのものが必ず遅いというわけではありません。

短い処理であれば、通常のメソッド呼び出しと同じように扱える場面も多いです。

ただし、LINQを使う場合は、書き方によっては余分な列挙やオブジェクト生成が発生することがあります。

C#
var result = users
.Where(user => user.IsActive)
.Select(user => user.Name)
.ToList();

通常の業務アプリでは、まず読みやすさを優先して問題ありません。

大量データを扱う場合やパフォーマンスが重要な箇所では、LINQとラムダ式の使い方、ToList() のタイミング、データベースへのクエリ変換などを意識する必要があります。

13-4. LINQを使わずにラムダ式だけ使える?

はい、LINQを使わなくてもラムダ式は使えます。

たとえば、Func に代入できます。

C#
Func<int, int> doubleNumber = x => x * 2;

Action に代入することもできます。

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

イベント処理でも使えます。

C#
button.Click += (sender, e) =>
{
Console.WriteLine("クリックされました");
};

ラムダ式はLINQ専用の機能ではありません。ただし、C#ではLINQと一緒に使われることが非常に多いです。

13-5. ラムダ式が読みにくいときはどうすればいい?

ラムダ式が読みにくいときは、無理に1行で理解しようとしないことが大切です。

まず、=> の左側と右側に分けて読みます。

C#
user => user.Age >= 20

これは、

userを受け取って
user.Age >= 20を判定する

という意味です。

次に、LINQメソッド名とセットで読みます。

C#
users.Where(user => user.Age >= 20)

これは、

usersから
user.Age >= 20を満たすものだけ取り出す

という意味です。

それでも読みにくい場合は、改行したり、メソッドに切り出したりしましょう。

C#
var adults = users.Where(IsAdult);

static bool IsAdult(User user)
{
return user.Age >= 20;
}

ラムダ式は短く書くための道具ですが、読みやすさを犠牲にする必要はありません。

まとめ

C#のラムダ式は、「その場で書ける小さな関数」です。

基本形は次のように表せます。

C#
引数 => 処理

たとえば、次のラムダ式は「nを受け取り、nが10より大きいか判定する」という意味です。

C#
n => n > 10

ラムダ式は、LINQと一緒に使われることが多く、WhereSelectOrderByAnyFirstOrDefault などで頻繁に登場します。

C#
var result = users
.Where(user => user.Age >= 20)
.Select(user => user.Name)
.ToList();

初心者が最初につまずきやすいポイントは、xn の正体、=> の意味、return の有無、波かっこの使い方です。

式形式では return を書かず、ステートメント形式で戻り値がある場合は return が必要です。

C#
Func<int, int> a = x => x * 2;

Func<int, int> b = x =>
{
return x * 2;
};

また、ラムダ式は短く書ける反面、複雑な処理を詰め込みすぎると読みにくくなります。引数名をわかりやすくし、長い条件は改行し、必要に応じてメソッドに切り出しましょう。

C#のラムダ式は、最初は難しく見えますが、引数 => 処理 という基本を押さえれば少しずつ読めるようになります。まずは WhereSelect の簡単な例から練習し、LINQのよくあるパターンに慣れていくことが、ラムダ式を理解する近道です。