C# lambda完全入門:ラムダ式の書き方・使い方・LINQでの実践例を初心者向けに解説
はじめに
C#を学び始めると、LINQやリスト操作のコードでよく登場するのが「lambda」、つまりラムダ式です。
C#var adults = users.Where(user => user.Age >= 20);
この user => user.Age >= 20 の部分がラムダ式です。最初は => の意味がわかりにくく、「急に見慣れない記号が出てきた」と感じるかもしれません。
しかし、C# lambdaを理解すると、条件に合うデータの抽出、並び替え、変換、イベント処理、非同期処理などを短く読みやすく書けるようになります。特にLINQを使ううえでは、ラムダ式の理解はほぼ必須といえます。
この記事では、C#のラムダ式について、基本構文から Func、Action、Predicate、LINQでの実践例、初心者がつまずきやすいポイントまで順番に解説します。
1. C#のlambda(ラムダ式)とは?
1-1. lambdaの基本概念を初心者向けにわかりやすく解説
C#のlambda、つまりラムダ式とは、簡単にいうと「名前のない小さな関数」をその場で書くための構文です。
通常、処理を関数として書く場合は、次のようにメソッド名を付けます。
C#int Square(int x)
{
return x * x;
}
これをラムダ式で書くと、次のようになります。
C#x => x * x
意味としては「xを受け取って、x * x を返す処理」です。
ラムダ式では、左側に引数、右側に処理を書きます。
C#引数 => 処理
たとえば、数値が偶数かどうかを判定するラムダ式は次のように書けます。
C#number => number % 2 == 0
これは「numberを受け取り、numberが2で割り切れるかどうかを返す」という意味です。
1-2. ラムダ式を使うと何が便利になるのか
ラムダ式を使うと、短い処理をわざわざ通常メソッドとして定義しなくても、その場で書けるようになります。
たとえば、リストから20歳以上のユーザーだけを取り出したい場合を考えます。
C#var adults = users.Where(user => user.Age >= 20);
このコードでは、user => user.Age >= 20 が「20歳以上かどうかを判定する条件」です。
通常メソッドで書くと、次のように別途メソッドを用意する必要があります。
C#bool IsAdult(User user)
{
return user.Age >= 20;
}
var adults = users.Where(IsAdult);
もちろん通常メソッドも悪くありません。しかし、1回しか使わない短い条件であれば、ラムダ式のほうが簡潔です。
ラムダ式の便利な点は主に次のとおりです。
C#var adults = users.Where(user => user.Age >= 20);
var names = users.Select(user => user.Name);
var sorted = users.OrderBy(user => user.Age);
条件、変換、並び替えの基準などを、その場で直感的に書けるのがC# lambdaの大きなメリットです。
1-3. 匿名メソッド・デリゲート・関数との違い
ラムダ式を理解するには、匿名メソッド、デリゲート、通常メソッドとの関係も知っておくと便利です。
まず、通常メソッドは名前を持つ処理です。
C#bool IsEven(int number)
{
return number % 2 == 0;
}
匿名メソッドは、名前を持たないメソッドです。
C#Predicate<int> isEven = delegate(int number)
{
return number % 2 == 0;
};
ラムダ式は、匿名メソッドをより短く書ける構文です。
C#Predicate<int> isEven = number => number % 2 == 0;
デリゲートは、メソッドやラムダ式を代入できる「関数を入れるための型」です。
C#Func<int, int> square = x => x * x;
この例では、Func<int, int> がデリゲート型で、x => x * x がラムダ式です。
つまり、関係を整理すると次のようになります。
C#// 通常メソッド
int Square(int x)
{
return x * x;
}
// 匿名メソッド
Func<int, int> square1 = delegate(int x)
{
return x * x;
};
// ラムダ式
Func<int, int> square2 = x => x * x;
初心者のうちは、「ラムダ式は名前のない関数を短く書く方法」「FuncやActionなどに代入できる」と理解しておけば問題ありません。
1-4. C#でlambdaがよく使われる場面
C# lambdaは、特に次のような場面でよく使われます。
LINQで条件を指定するときは、ラムダ式が頻繁に登場します。
C#var expensiveProducts = products.Where(product => product.Price >= 1000);
データを変換するときにも使います。
C#var names = users.Select(user => user.Name);
並び替えの基準を指定するときにも使います。
C#var sortedUsers = users.OrderBy(user => user.Age);
イベント処理でも使われます。
C#button.Click += (sender, e) =>
{
Console.WriteLine("ボタンがクリックされました");
};
非同期処理でもラムダ式はよく使われます。
C#await Task.Run(() =>
{
Console.WriteLine("バックグラウンドで処理します");
});
このように、C# lambdaは日常的なC#開発で非常によく使われます。
2. C#ラムダ式の基本的な書き方
2-1. ラムダ式の基本構文:引数 => 処理
ラムダ式の基本構文は次の形です。
C#引数 => 処理
たとえば、受け取った数値を2倍にするラムダ式は次のように書けます。
C#x => x * 2
これは「xを受け取り、x * 2 を返す」という意味です。
ラムダ式は、単体で書くよりも、Func や Action、LINQメソッドと組み合わせて使うことが多いです。
C#Func<int, int> doubleNumber = x => x * 2;
Console.WriteLine(doubleNumber(5)); // 10
この例では、doubleNumber という変数にラムダ式を代入しています。
2-2. 引数が1つの場合の書き方
引数が1つの場合、引数の丸かっこは省略できます。
C#Func<int, int> square = x => x * x;
このコードは、次のように書いても同じです。
C#Func<int, int> square = (x) => x * x;
一般的には、引数が1つで型を明示しない場合は、丸かっこを省略することが多いです。
C#var adults = users.Where(user => user.Age >= 20);
ただし、引数の型を明示する場合は丸かっこが必要です。
C#Func<int, int> square = (int x) => x * x;
2-3. 引数が複数ある場合の書き方
引数が複数ある場合は、丸かっこで囲みます。
C#Func<int, int, int> add = (x, y) => x + y;
これは「xとyを受け取り、x + y を返す」という意味です。
実行すると次のようになります。
C#Console.WriteLine(add(3, 5)); // 8
LINQでは、インデックス付きの Select などで複数引数のラムダ式を使うことがあります。
C#var indexedNames = names.Select((name, index) => $"{index}: {name}");
この例では、name が要素、index が要素番号です。
2-4. 引数がない場合の書き方
引数がないラムダ式では、空の丸かっこを書きます。
C#Action sayHello = () => Console.WriteLine("Hello");
実行すると、次のようになります。
C#sayHello(); // Hello
引数なしで値を返すラムダ式も書けます。
C#Func<DateTime> getNow = () => DateTime.Now;
Console.WriteLine(getNow());
このように、引数がない場合は必ず () を書きます。
2-5. 戻り値があるラムダ式の書き方
戻り値があるラムダ式は、右側に返したい式を書きます。
C#Func<int, int> triple = x => x * 3;
この場合、x * 3 の結果がそのまま戻り値になります。
C#Console.WriteLine(triple(4)); // 12
複数行で戻り値を返す場合は、波かっこと return を使います。
C#Func<int, int> triple = x =>
{
int result = x * 3;
return result;
};
式形式のラムダ式では return を書かず、ステートメント形式のラムダ式では必要に応じて return を書く、と覚えるとわかりやすいです。
2-6. 型推論と明示的な型指定の使い分け
C#のラムダ式では、多くの場合、引数の型を省略できます。
C#Func<int, int> square = x => x * x;
この場合、左側の Func<int, int> によって、x が int 型だと推論されます。
明示的に型を書くこともできます。
C#Func<int, int> square = (int x) => x * x;
LINQでも型推論がよく使われます。
C#var adults = users.Where(user => user.Age >= 20);
ここでは、users が List<User> であれば、user は User 型だと推論されます。
基本的には、型推論で読みやすい場合は型を省略して問題ありません。一方で、型がわかりにくい場合や、コンパイラが型を推論できない場合は明示的に書くとよいでしょう。
C#Func<string, bool> isLongText = (string text) => text.Length >= 10;
初心者のうちは、まず型を明示して理解し、慣れてきたら省略する書き方にするとスムーズです。
3. C#ラムダ式の種類
3-1. 式形式のラムダ式とは
式形式のラムダ式とは、右側に1つの式を書くラムダ式です。
C#x => x * x
たとえば、次のように使います。
C#Func<int, int> square = x => x * x;
条件式を書くこともできます。
C#Predicate<int> isEven = x => x % 2 == 0;
LINQでは、式形式のラムダ式がよく使われます。
C#var adults = users.Where(user => user.Age >= 20);
var names = users.Select(user => user.Name);
式形式のラムダ式は短く書けるため、単純な条件や変換に向いています。
3-2. ステートメント形式のラムダ式とは
ステートメント形式のラムダ式とは、右側に { } を使って複数の処理を書くラムダ式です。
C#x =>
{
int result = x * x;
return result;
}
たとえば、次のように書けます。
C#Func<int, int> square = x =>
{
int result = x * x;
return result;
};
ログ出力など、複数の処理を行いたい場合に便利です。
C#Action<string> printMessage = message =>
{
Console.WriteLine("メッセージを出力します");
Console.WriteLine(message);
};
ステートメント形式では、通常のメソッド本体のように複数行の処理を書けます。
3-3. 複数行のラムダ式を書く方法
複数行のラムダ式を書く場合は、波かっこ { } を使います。
C#Func<int, string> judgeScore = score =>
{
if (score >= 80)
{
return "合格";
}
return "不合格";
};
このラムダ式は、点数を受け取り、80点以上なら「合格」、それ以外なら「不合格」を返します。
実行例は次のとおりです。
C#Console.WriteLine(judgeScore(90)); // 合格
Console.WriteLine(judgeScore(60)); // 不合格
複数行のラムダ式では、if、for、try-catch などの制御構文も使えます。
C#Action<List<int>> printNumbers = numbers =>
{
foreach (var number in numbers)
{
Console.WriteLine(number);
}
};
ただし、あまりに長いラムダ式は可読性が下がります。複雑な処理は通常メソッドに切り出すことを検討しましょう。
3-4. return文を使うラムダ式
ステートメント形式のラムダ式で値を返す場合は、return 文を使います。
C#Func<int, int> calculate = x =>
{
int result = x * 10;
return result;
};
一方、式形式のラムダ式では return は書きません。
C#Func<int, int> calculate = x => x * 10;
次のように式形式で return を書くとエラーになります。
C#// エラー
Func<int, int> calculate = x => return x * 10;
return を使いたい場合は、必ず { } を使ってステートメント形式にします。
C#Func<int, int> calculate = x =>
{
return x * 10;
};
3-5. 式形式とステートメント形式の使い分け
式形式は、処理が短くシンプルな場合に向いています。
C#var names = users.Select(user => user.Name);
ステートメント形式は、複数の処理が必要な場合に向いています。
C#var results = users.Select(user =>
{
var label = user.Age >= 20 ? "成人" : "未成年";
return $"{user.Name}:{label}";
});
ただし、ステートメント形式が長くなりすぎる場合は、通常メソッドに切り出したほうが読みやすくなります。
C#var results = users.Select(CreateUserLabel);
string CreateUserLabel(User user)
{
var label = user.Age >= 20 ? "成人" : "未成年";
return $"{user.Name}:{label}";
}
目安としては、1行で自然に読める処理なら式形式、複数の分岐や計算が必要ならステートメント形式、さらに複雑なら通常メソッドにするのがおすすめです。
4. Func・Action・Predicateでラムダ式を使う方法
4-1. Funcとは何か
Func は、戻り値がある処理を表すデリゲート型です。
C#Func<int, int> square = x => x * x;
この例では、Func<int, int> は「intを受け取り、intを返す関数」を意味します。
C#Console.WriteLine(square(5)); // 25
複数の引数を受け取ることもできます。
C#Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(3, 4)); // 7
Func の最後の型が戻り値の型です。
C#Func<引数1の型, 引数2の型, 戻り値の型>
たとえば、文字列を受け取って文字数を返す場合は次のようになります。
C#Func<string, int> getLength = text => text.Length;
4-2. Actionとは何か
Action は、戻り値がない処理を表すデリゲート型です。
C#Action<string> print = message => Console.WriteLine(message);
この例では、文字列を受け取ってコンソールに出力しますが、値は返しません。
C#print("Hello");
引数なしの Action も書けます。
C#Action sayHello = () => Console.WriteLine("Hello");
複数の引数を受け取る Action もあります。
C#Action<string, int> printUser = (name, age) =>
{
Console.WriteLine($"{name}さんは{age}歳です");
};
printUser("田中", 25);
「何かを実行するだけで結果を返さない処理」には Action を使います。
4-3. Predicateとは何か
Predicate<T> は、指定した型の値を受け取り、bool を返すデリゲート型です。
C#Predicate<int> isEven = number => number % 2 == 0;
この例では、数値が偶数かどうかを判定します。
C#Console.WriteLine(isEven(4)); // True
Console.WriteLine(isEven(5)); // False
Predicate<T> は、条件判定に使うとわかりやすい型です。
C#Predicate<string> isLongText = text => text.Length >= 10;
実際には、Predicate<T> は次の Func<T, bool> と似ています。
C#Func<int, bool> isEvenFunc = number => number % 2 == 0;
Predicate<int> isEvenPredicate = number => number % 2 == 0;
どちらも「intを受け取り、boolを返す処理」を表せます。
4-4. FuncとActionの違い
Func と Action の大きな違いは、戻り値があるかどうかです。
Func は戻り値があります。
C#Func<int, int> square = x => x * x;
int result = square(5);
Action は戻り値がありません。
C#Action<string> print = message => Console.WriteLine(message);
print("Hello");
値を返す処理なら Func、実行するだけの処理なら Action と考えるとわかりやすいです。
C#// 値を返す
Func<int, bool> isAdultAge = age => age >= 20;
// 値を返さない
Action<string> log = message => Console.WriteLine(message);
4-5. ラムダ式を変数に代入する方法
ラムダ式は、Func、Action、Predicate などの変数に代入できます。
C#Func<int, int> doubleNumber = x => x * 2;
実行するときは、通常のメソッドのように呼び出します。
C#int result = doubleNumber(10);
Console.WriteLine(result); // 20
文字列を加工するラムダ式も変数に代入できます。
C#Func<string, string> addGreeting = name => $"こんにちは、{name}さん";
Console.WriteLine(addGreeting("佐藤"));
戻り値がない処理なら Action を使います。
C#Action<string> showMessage = message =>
{
Console.WriteLine($"メッセージ: {message}");
};
showMessage("処理が完了しました");
条件判定なら Predicate を使えます。
C#Predicate<int> isPositive = number => number > 0;
Console.WriteLine(isPositive(10)); // True
Console.WriteLine(isPositive(-1)); // False
4-6. メソッドの引数としてラムダ式を渡す方法
ラムダ式は、メソッドの引数として渡すこともできます。
たとえば、数値リストから条件に合うものを表示するメソッドを作ります。
C#void PrintNumbers(List<int> numbers, Predicate<int> condition)
{
foreach (var number in numbers)
{
if (condition(number))
{
Console.WriteLine(number);
}
}
}
このメソッドにラムダ式を渡します。
C#var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
PrintNumbers(numbers, number => number % 2 == 0);
この場合、偶数だけが表示されます。
別の条件も簡単に渡せます。
C#PrintNumbers(numbers, number => number >= 4);
このように、ラムダ式を引数として渡せると、処理の一部を外から差し替えられる柔軟なコードを書けます。
5. LINQで使うC# lambdaの実践例
5-1. Whereで条件に一致するデータを抽出する
LINQの Where は、条件に一致する要素だけを抽出するメソッドです。
C#var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers.Where(number => number % 2 == 0);
この例では、偶数だけを取り出しています。
C#foreach (var number in evenNumbers)
{
Console.WriteLine(number);
}
出力結果は次のようになります。
C#2
4
6
オブジェクトのリストでもよく使います。
C#var adults = users.Where(user => user.Age >= 20);
このコードは、ユーザー一覧から20歳以上のユーザーだけを抽出します。
5-2. Selectでデータを変換する
Select は、各要素を別の形に変換するメソッドです。
C#var names = users.Select(user => user.Name);
この例では、ユーザー一覧から名前だけを取り出しています。
数値を変換する例も見てみましょう。
C#var numbers = new List<int> { 1, 2, 3 };
var doubled = numbers.Select(number => number * 2);
結果は次のようになります。
C#2
4
6
オブジェクトを文字列に変換することもできます。
C#var labels = users.Select(user => $"{user.Name}さん({user.Age}歳)");
Select とラムダ式を組み合わせると、リストの形を簡単に変換できます。
5-3. OrderByで並び替える
OrderBy は、指定した基準で昇順に並び替えるメソッドです。
C#var sortedUsers = users.OrderBy(user => user.Age);
この例では、年齢が若い順に並び替えています。
降順にしたい場合は OrderByDescending を使います。
C#var sortedUsers = users.OrderByDescending(user => user.Age);
名前順に並び替えることもできます。
C#var sortedByName = users.OrderBy(user => user.Name);
複数条件で並び替える場合は、ThenBy を使います。
C#var sorted = users
.OrderBy(user => user.Age)
.ThenBy(user => user.Name);
このコードでは、まず年齢順に並び替え、同じ年齢の場合は名前順に並び替えます。
5-4. FirstOrDefaultで最初の要素を取得する
FirstOrDefault は、条件に一致する最初の要素を取得するメソッドです。
C#var user = users.FirstOrDefault(user => user.Name == "田中");
この例では、名前が「田中」の最初のユーザーを取得します。
一致する要素がない場合、参照型では null が返ることがあります。そのため、nullチェックを行うのが安全です。
C#if (user != null)
{
Console.WriteLine(user.Name);
}
else
{
Console.WriteLine("ユーザーが見つかりませんでした");
}
数値リストでも使えます。
C#var numbers = new List<int> { 10, 20, 30 };
int first = numbers.FirstOrDefault(number => number >= 20);
この場合、20 が取得されます。
5-5. Any・Allで条件判定する
Any は、条件に一致する要素が1つでもあるかどうかを判定します。
C#bool hasAdult = users.Any(user => user.Age >= 20);
この例では、20歳以上のユーザーが1人でもいれば true になります。
All は、すべての要素が条件を満たすかどうかを判定します。
C#bool allAdults = users.All(user => user.Age >= 20);
この例では、全員が20歳以上の場合だけ true になります。
数値リストでもよく使います。
C#var numbers = new List<int> { 2, 4, 6 };
bool allEven = numbers.All(number => number % 2 == 0);
bool hasOdd = numbers.Any(number => number % 2 != 0);
Any と All は、条件チェックを簡潔に書きたいときに便利です。
5-6. Countで条件に合う件数を数える
Count にラムダ式を渡すと、条件に合う要素数を数えられます。
C#int adultCount = users.Count(user => user.Age >= 20);
この例では、20歳以上のユーザー数を数えています。
数値リストの例です。
C#var numbers = new List<int> { 1, 2, 3, 4, 5 };
int evenCount = numbers.Count(number => number % 2 == 0);
この場合、偶数は 2 と 4 なので、結果は 2 です。
条件なしで全件数を数える場合は、ラムダ式を渡さずに書けます。
C#int totalCount = users.Count();
条件付きで数えたいときは、Count(条件) と覚えておくとよいでしょう。
5-7. List・配列・コレクションでの実用サンプル
C# lambdaは、List、配列、コレクションなどで幅広く使えます。
まず、List<int> の例です。
C#var numbers = new List<int> { 10, 15, 20, 25, 30 };
var results = numbers
.Where(number => number >= 20)
.Select(number => number * 2);
foreach (var result in results)
{
Console.WriteLine(result);
}
このコードでは、20以上の数値を抽出し、それぞれ2倍にしています。
配列でも使えます。
C#string[] names = { "Tanaka", "Sato", "Suzuki" };
var shortNames = names.Where(name => name.Length <= 5);
オブジェクトのコレクションでもよく使います。
C#var activeUserNames = users
.Where(user => user.IsActive)
.OrderBy(user => user.Name)
.Select(user => user.Name);
このコードは、有効なユーザーだけを抽出し、名前順に並び替え、名前だけを取得しています。
LINQとラムダ式を組み合わせることで、コレクション操作を読みやすく書けます。
6. C# lambdaを使った実践的なコード例
6-1. リスト検索でラムダ式を使う例
リストから条件に合う要素を検索する場合、ラムダ式はとても便利です。
C#var users = new List<User>
{
new User { Id = 1, Name = "田中", Age = 25 },
new User { Id = 2, Name = "佐藤", Age = 18 },
new User { Id = 3, Name = "鈴木", Age = 30 }
};
var user = users.FirstOrDefault(user => user.Id == 2);
この例では、Id が 2 のユーザーを検索しています。
C#if (user != null)
{
Console.WriteLine(user.Name);
}
複数条件で検索することもできます。
C#var targetUser = users.FirstOrDefault(user => user.Age >= 20 && user.Name == "田中");
&& を使えばAND条件、|| を使えばOR条件を書けます。
C#var result = users.Where(user => user.Age >= 20 || user.Name == "佐藤");
6-2. 条件分岐をラムダ式で書く例
ラムダ式の中で条件分岐を書くこともできます。
短い条件分岐なら三項演算子を使うと簡潔です。
C#Func<int, string> judgeAge = age => age >= 20 ? "成人" : "未成年";
実行例です。
C#Console.WriteLine(judgeAge(25)); // 成人
Console.WriteLine(judgeAge(18)); // 未成年
複数行で書きたい場合は、ステートメント形式にします。
C#Func<int, string> judgeScore = score =>
{
if (score >= 80)
{
return "優";
}
else if (score >= 60)
{
return "可";
}
else
{
return "不可";
}
};
処理が短ければラムダ式で問題ありませんが、条件が複雑になる場合は通常メソッドに切り出したほうが読みやすくなります。
6-3. ソート処理をラムダ式で書く例
リストの並び替えでもラムダ式はよく使われます。
C#var sortedUsers = users.OrderBy(user => user.Age);
年齢の降順にしたい場合は次のように書きます。
C#var sortedUsers = users.OrderByDescending(user => user.Age);
名前順に並び替える場合です。
C#var sortedByName = users.OrderBy(user => user.Name);
複数条件のソートもできます。
C#var sorted = users
.OrderByDescending(user => user.IsActive)
.ThenBy(user => user.Age)
.ThenBy(user => user.Name);
この例では、有効なユーザーを先に並べ、その中で年齢順、さらに名前順に並び替えています。
List<T>.Sort でもラムダ式を使えます。
C#users.Sort((x, y) => x.Age.CompareTo(y.Age));
このコードは、年齢の昇順でリストを直接並び替えます。
6-4. イベント処理でラムダ式を使う例
C#では、イベント処理にもラムダ式を使えます。
たとえば、ボタンクリック時の処理です。
C#button.Click += (sender, e) =>
{
Console.WriteLine("ボタンがクリックされました");
};
イベントハンドラーを通常メソッドとして書く場合は、次のようになります。
C#button.Click += Button_Click;
void Button_Click(object sender, EventArgs e)
{
Console.WriteLine("ボタンがクリックされました");
}
短い処理であれば、ラムダ式を使うことでコードをコンパクトにできます。
ただし、イベント解除が必要な場合は注意が必要です。匿名のラムダ式をその場で書くと、あとから解除しにくくなります。
C#EventHandler handler = (sender, e) =>
{
Console.WriteLine("クリックされました");
};
button.Click += handler;
button.Click -= handler;
イベントを解除する可能性がある場合は、ラムダ式を変数に入れておくと安全です。
6-5. 非同期処理でラムダ式を使う例
非同期処理でもラムダ式はよく使われます。
C#await Task.Run(() =>
{
Console.WriteLine("重い処理を実行します");
});
Task.Run に渡している () => { ... } がラムダ式です。
非同期ラムダ式を書く場合は、async を付けます。
C#Func<Task> loadDataAsync = async () =>
{
await Task.Delay(1000);
Console.WriteLine("データを読み込みました");
};
await loadDataAsync();
引数を受け取る非同期ラムダ式も書けます。
C#Func<string, Task> sendMessageAsync = async message =>
{
await Task.Delay(500);
Console.WriteLine(message);
};
await sendMessageAsync("送信しました");
非同期ラムダ式は便利ですが、例外処理や戻り値の型に注意が必要です。async void はイベントハンドラー以外では避け、基本的には Task や Task<T> を返す形にしましょう。
6-6. メソッドチェーンとラムダ式を組み合わせる例
LINQでは、メソッドチェーンとラムダ式を組み合わせることで、データ処理を流れるように書けます。
C#var results = users
.Where(user => user.IsActive)
.Where(user => user.Age >= 20)
.OrderBy(user => user.Name)
.Select(user => new
{
user.Name,
user.Age
});
このコードは、次の処理を順番に行っています。
有効なユーザーだけを抽出し、20歳以上に絞り込み、名前順に並び替え、名前と年齢だけを持つ形に変換しています。
メソッドチェーンを使うと、処理の流れが上から下に読めるため、可読性が高くなります。
ただし、1行に詰め込みすぎると読みにくくなります。
C#var results = users.Where(user => user.IsActive).Where(user => user.Age >= 20).OrderBy(user => user.Name).Select(user => user.Name);
このような場合は、改行して書くのがおすすめです。
C#var results = users
.Where(user => user.IsActive)
.Where(user => user.Age >= 20)
.OrderBy(user => user.Name)
.Select(user => user.Name);
7. C# lambdaで初心者がつまずきやすいポイント
7-1. => 演算子の意味がわからない
ラムダ式で最初につまずきやすいのが => です。
C#x => x * 2
これは「xを受け取って、x * 2 を返す」と読みます。
左側が入力、右側が処理です。
C#入力 => 処理
たとえば、次のコードは「userを受け取り、user.Age >= 20 の結果を返す」という意味です。
C#user => user.Age >= 20
=> は「ならば」「を使って」という感覚で読むと理解しやすいです。
C#user => user.Name
これは「userからNameを取り出す」と読むことができます。
7-2. 引数と戻り値の型がわからない
ラムダ式は型が省略されることが多いため、初心者には型が見えにくいです。
C#var adults = users.Where(user => user.Age >= 20);
この場合、users が List<User> なら、user は User 型です。そして user.Age >= 20 は bool を返します。
つまり、次のようなイメージです。
C#Func<User, bool> condition = user => user.Age >= 20;
Where は、各要素に対して true または false を返す条件を受け取ります。
Select の場合は、変換後の型が戻り値になります。
C#var names = users.Select(user => user.Name);
この場合、user は User 型、user.Name は string 型です。
型がわからなくなったら、「左側の引数は何型か」「右側の結果は何型か」を考えると理解しやすくなります。
7-3. returnを書ける場合と書けない場合
ラムダ式では、return を書ける場合と書けない場合があります。
式形式では return を書きません。
C#Func<int, int> square = x => x * x;
これは x * x の結果が自動的に戻り値になります。
一方、ステートメント形式では return を書けます。
C#Func<int, int> square = x =>
{
return x * x;
};
次の書き方はエラーです。
C#// エラー
Func<int, int> square = x => return x * x;
return を使うなら { } を付ける、と覚えておきましょう。
C#Func<int, int> square = x =>
{
return x * x;
};
7-4. ラムダ式の中で外側の変数を使う場合の注意点
ラムダ式では、外側の変数を使うことができます。
C#int minAge = 20;
var adults = users.Where(user => user.Age >= minAge);
このように、ラムダ式の外で定義した minAge を中で参照できます。
ただし、外側の変数をあとから変更すると、意図しない結果になることがあります。
C#int threshold = 20;
Func<int, bool> isOverThreshold = age => age >= threshold;
threshold = 30;
Console.WriteLine(isOverThreshold(25)); // False
ラムダ式は threshold の値そのものではなく、変数を参照しています。そのため、あとから threshold を変更すると、ラムダ式の結果も変わります。
ループの中でラムダ式を作る場合も注意が必要です。
C#var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
int current = i;
actions.Add(() => Console.WriteLine(current));
}
foreach (var action in actions)
{
action();
}
このように、ループ内で別の変数にコピーしておくと意図が明確になります。
7-5. null参照や例外が発生しやすいケース
ラムダ式の中でも、通常のC#コードと同じようにnull参照が発生することがあります。
C#var usersWithLongName = users.Where(user => user.Name.Length >= 5);
もし user.Name が null の場合、NullReferenceException が発生します。
安全に書くなら、nullチェックを入れます。
C#var usersWithLongName = users.Where(user => user.Name != null && user.Name.Length >= 5);
null条件演算子を使う方法もあります。
C#var usersWithLongName = users.Where(user => user.Name?.Length >= 5);
ただし、この書き方では結果が bool? になるケースがあるため、必要に応じて明確に比較します。
C#var usersWithLongName = users.Where(user => (user.Name?.Length ?? 0) >= 5);
FirstOrDefault の結果にも注意が必要です。
C#var user = users.FirstOrDefault(user => user.Id == 10);
Console.WriteLine(user.Name); // userがnullならエラー
安全に書くなら、次のようにします。
C#if (user != null)
{
Console.WriteLine(user.Name);
}
7-6. 可読性が下がるラムダ式の書き方
ラムダ式は便利ですが、何でも短く書けばよいわけではありません。
次のように条件が長すぎると読みにくくなります。
C#var results = users.Where(user => user.IsActive && user.Age >= 20 && user.LastLoginDate >= DateTime.Today.AddDays(-30) && user.Role != "Guest");
この場合は、改行するか、条件をメソッド化すると読みやすくなります。
C#var results = users.Where(user =>
user.IsActive &&
user.Age >= 20 &&
user.LastLoginDate >= DateTime.Today.AddDays(-30) &&
user.Role != "Guest");
さらにわかりやすくするなら、通常メソッドに切り出します。
C#var results = users.Where(IsTargetUser);
bool IsTargetUser(User user)
{
return user.IsActive &&
user.Age >= 20 &&
user.LastLoginDate >= DateTime.Today.AddDays(-30) &&
user.Role != "Guest";
}
ラムダ式は「短く書ける」ことがメリットですが、「読みやすい」ことが最優先です。
8. ラムダ式・匿名メソッド・通常メソッドの比較
8-1. 通常メソッドで書いた場合
まず、通常メソッドで条件判定を書く例を見てみましょう。
C#bool IsAdult(User user)
{
return user.Age >= 20;
}
var adults = users.Where(IsAdult);
通常メソッドのメリットは、名前を付けられることです。IsAdult という名前があるため、何をしている処理なのかがわかりやすくなります。
また、複雑な処理や複数箇所で再利用する処理にも向いています。
C#bool IsValidUser(User user)
{
if (!user.IsActive)
{
return false;
}
if (user.Age < 20)
{
return false;
}
return true;
}
処理が長くなる場合は、ラムダ式より通常メソッドのほうが読みやすいです。
8-2. 匿名メソッドで書いた場合
匿名メソッドを使うと、名前のない処理をその場で書けます。
C#var adults = users.Where(delegate(User user)
{
return user.Age >= 20;
});
これはラムダ式が広く使われる前によく使われていた書き方です。
Predicate に代入する場合は次のように書けます。
C#Predicate<User> isAdult = delegate(User user)
{
return user.Age >= 20;
};
匿名メソッドでも目的は達成できますが、ラムダ式に比べるとやや冗長です。
8-3. ラムダ式で書いた場合
同じ処理をラムダ式で書くと、かなり短くなります。
C#var adults = users.Where(user => user.Age >= 20);
Predicate に代入する場合も簡潔です。
C#Predicate<User> isAdult = user => user.Age >= 20;
ラムダ式は、短い条件や変換を書く場合に非常に向いています。
C#var names = users.Select(user => user.Name);
var sorted = users.OrderBy(user => user.Age);
LINQと組み合わせる場合、ラムダ式はもっとも一般的な書き方です。
8-4. どの書き方を選ぶべきか
基本的には、短い処理ならラムダ式、複雑な処理なら通常メソッドを選ぶのがおすすめです。
たとえば、次のような短い条件ならラムダ式で十分です。
C#var adults = users.Where(user => user.Age >= 20);
一方、条件が複雑な場合は通常メソッドにします。
C#var targetUsers = users.Where(IsTargetUser);
C#bool IsTargetUser(User user)
{
return user.IsActive &&
user.Age >= 20 &&
user.LastLoginDate >= DateTime.Today.AddDays(-30);
}
匿名メソッドは現在でも使えますが、特別な理由がなければラムダ式を使うことが多いです。
8-5. 初心者がまず覚えるべき使い分け
初心者が最初に覚えるべき使い分けは、次のような感覚です。
短い条件はラムダ式で書きます。
C#user => user.Age >= 20
短い変換もラムダ式で書きます。
C#user => user.Name
処理が複数行になる場合は、ステートメント形式にします。
C#user =>
{
var label = user.Age >= 20 ? "成人" : "未成年";
return $"{user.Name}:{label}";
}
さらに複雑な処理は通常メソッドに切り出します。
C#var labels = users.Select(CreateUserLabel);
この使い分けができれば、C# lambdaの基本的な使い方としては十分です。
9. C# lambdaを読みやすく書くためのベストプラクティス
9-1. 短くシンプルな処理に使う
ラムダ式は、短くシンプルな処理に使うと効果的です。
C#var adults = users.Where(user => user.Age >= 20);
このようなコードは、何をしているかがすぐにわかります。
逆に、長すぎるラムダ式は読みづらくなります。
C#var results = users.Select(user =>
{
var tax = user.Price * 0.1m;
var total = user.Price + tax;
var discount = total >= 10000 ? 1000 : 0;
return total - discount;
});
この程度ならまだ読めますが、さらに複雑になる場合はメソッド化したほうがよいです。
9-2. 複雑な処理はメソッド化する
ラムダ式の中に複雑なロジックを詰め込むと、コードの見通しが悪くなります。
C#var results = orders.Select(order => CalculateTotalPrice(order));
または、メソッドグループとしてさらに簡潔に書けます。
C#var results = orders.Select(CalculateTotalPrice);
C#decimal CalculateTotalPrice(Order order)
{
var tax = order.Price * 0.1m;
var total = order.Price + tax;
if (total >= 10000)
{
total -= 1000;
}
return total;
}
メソッド化すると、処理に名前を付けられるため、コードの意図が伝わりやすくなります。
9-3. 変数名をわかりやすくする
ラムダ式では、引数名を短くしすぎると意味がわかりにくくなることがあります。
C#var results = users.Where(x => x.Age >= 20);
この程度なら問題ありませんが、複雑な処理では x よりも意味のある名前を使うほうが読みやすいです。
C#var results = users.Where(user => user.Age >= 20);
商品なら product、注文なら order、顧客なら customer のように、データの意味に合った名前を付けましょう。
C#var expensiveProducts = products.Where(product => product.Price >= 10000);
var recentOrders = orders.Where(order => order.OrderDate >= DateTime.Today.AddDays(-7));
変数名を丁寧に付けるだけで、ラムダ式の読みやすさは大きく変わります。
9-4. LINQと組み合わせるときの注意点
LINQとラムダ式を組み合わせると便利ですが、処理の実行タイミングに注意が必要です。
C#var query = users.Where(user => user.Age >= 20);
この時点では、まだ実際に抽出処理が実行されない場合があります。foreach で列挙したときや、ToList() を呼んだときに実行されます。
C#var adults = users
.Where(user => user.Age >= 20)
.ToList();
結果をすぐに確定させたい場合は、ToList() や ToArray() を使います。
また、メソッドチェーンが長くなりすぎる場合は、適度に改行しましょう。
C#var results = users
.Where(user => user.IsActive)
.OrderBy(user => user.Name)
.Select(user => user.Name)
.ToList();
LINQは便利ですが、複雑な処理を無理に1つのチェーンに詰め込まないことが大切です。
9-5. チーム開発で読みやすいラムダ式を書くコツ
チーム開発では、自分だけでなく他のメンバーが読んでも理解しやすいコードを書くことが重要です。
ラムダ式を書くときは、まず「その場で書いたほうがわかりやすいか」を考えましょう。
C#var activeUsers = users.Where(user => user.IsActive);
このような処理は、その場にラムダ式があるほうが読みやすいです。
一方で、業務ルールが含まれる条件はメソッド化したほうがよい場合があります。
C#var targetUsers = users.Where(IsCampaignTarget);
C#bool IsCampaignTarget(User user)
{
return user.IsActive &&
user.Age >= 20 &&
user.HasAgreedToEmail;
}
IsCampaignTarget という名前があることで、単なる条件式ではなく「キャンペーン対象者を判定している」ことが伝わります。
チーム開発では、短さよりも意図の伝わりやすさを優先しましょう。
10. C# lambdaに関するよくある質問
10-1. C#のlambdaとLINQは同じもの?
C#のlambdaとLINQは同じものではありません。
ラムダ式は、名前のない関数を短く書くための構文です。
C#user => user.Age >= 20
LINQは、コレクションやデータソースに対して、検索、抽出、変換、並び替えなどを行うための仕組みです。
C#var adults = users.Where(user => user.Age >= 20);
この例では、Where がLINQのメソッドで、user => user.Age >= 20 がラムダ式です。
つまり、LINQの中でラムダ式がよく使われるだけで、両者は別のものです。
10-2. ラムダ式はいつ使うべき?
ラムダ式は、短い条件、変換、並び替え、イベント処理などをその場で書きたいときに使うのが向いています。
C#var adults = users.Where(user => user.Age >= 20);
var names = users.Select(user => user.Name);
var sorted = users.OrderBy(user => user.Age);
一方で、処理が長い場合や、複数箇所で再利用する場合、業務上の意味を明確にしたい場合は通常メソッドにするのがおすすめです。
C#var targetUsers = users.Where(IsTargetUser);
短く書けるから使うのではなく、読みやすくなる場面で使うことが大切です。
10-3. ラムダ式を使わなくてもC#は書ける?
ラムダ式を使わなくてもC#のコードは書けます。
たとえば、通常メソッドを使って同じ処理を書くことができます。
C#bool IsAdult(User user)
{
return user.Age >= 20;
}
var adults = users.Where(IsAdult);
ただし、LINQやイベント処理、非同期処理などではラムダ式が非常によく使われます。そのため、現代的なC#コードを読むためには、ラムダ式を理解しておくことが重要です。
最初からすべてを完璧に覚える必要はありません。まずは x => x * 2 や user => user.Age >= 20 のような基本形から慣れていきましょう。
10-4. Funcとラムダ式の違いは?
Func は、戻り値がある処理を表すデリゲート型です。ラムダ式は、その Func に代入できる処理そのものです。
C#Func<int, int> square = x => x * x;
この例では、Func<int, int> が型で、x => x * x がラムダ式です。
別の言い方をすると、Func は「入れ物」、ラムダ式は「中に入れる処理」です。
Action や Predicate にもラムダ式を代入できます。
C#Action<string> print = message => Console.WriteLine(message);
Predicate<int> isEven = number => number % 2 == 0;
10-5. ラムダ式は初心者でも覚えるべき?
C#を学ぶ初心者でも、ラムダ式は早めに覚えておくのがおすすめです。
理由は、LINQの学習でほぼ必ず登場するからです。
C#var adults = users.Where(user => user.Age >= 20);
また、実務のC#コードでも、ラムダ式は非常によく使われます。
最初は難しく感じるかもしれませんが、基本は次の形だけです。
C#引数 => 処理
まずは、次のような短い例を読めるようにしましょう。
C#x => x * 2
user => user.Name
user => user.Age >= 20
これらが理解できれば、LINQのコードもかなり読みやすくなります。
10-6. ラムダ式でデバッグはできる?
ラムダ式もデバッグできます。
Visual Studioなどの開発環境では、ラムダ式を含む行にブレークポイントを設定したり、ステートメント形式のラムダ式の中で変数の値を確認したりできます。
C#var results = users.Where(user =>
{
bool isAdult = user.Age >= 20;
return isAdult;
});
このように一時変数を使って複数行にすると、デバッグしやすくなります。
短い式形式のラムダ式は便利ですが、デバッグしにくいと感じた場合は、いったんステートメント形式に書き換えるとよいでしょう。
C#var results = users.Where(user =>
{
return user.Age >= 20;
});
さらに複雑な場合は、通常メソッドに切り出すとデバッグしやすくなります。
C#var results = users.Where(IsAdult);
bool IsAdult(User user)
{
return user.Age >= 20;
}
まとめ
C# lambda、つまりラムダ式は、名前のない小さな関数をその場で書くための構文です。
基本形は次のとおりです。
C#引数 => 処理
たとえば、数値を2倍にするラムダ式は次のように書けます。
C#x => x * 2
ユーザーが20歳以上かどうかを判定するラムダ式は次のようになります。
C#user => user.Age >= 20
ラムダ式は、Func、Action、Predicate に代入したり、LINQの Where、Select、OrderBy、FirstOrDefault、Any、All、Count などで使ったりできます。
C#var adults = users.Where(user => user.Age >= 20);
var names = users.Select(user => user.Name);
var sorted = users.OrderBy(user => user.Age);
式形式のラムダ式は短い処理に向いており、ステートメント形式のラムダ式は複数行の処理に向いています。
C#Func<int, int> square = x => x * x;
Func<int, int> square2 = x =>
{
return x * x;
};
ただし、ラムダ式は短く書ける反面、複雑な処理を詰め込みすぎると可読性が下がります。短くシンプルな処理にはラムダ式を使い、複雑な処理や再利用したい処理は通常メソッドに切り出すのがよい使い分けです。
C# lambdaを理解すると、LINQやコレクション操作が一気に読みやすくなります。まずは x => x * 2、user => user.Name、user => user.Age >= 20 のような基本的なラムダ式から慣れていきましょう。

