前回は、コレクションの基本について学びました。
今回はその続きとして、リストなどのコレクションをより便利に扱えるLINQという機能について解説します。
LINQは、実務で非常によく使われる必須の機能であり、C#開発において基本を押さえておくことが大きな強みになります。
LINQとは?
LINQ(リンク)とは、リストなどのコレクションを簡単に操作するための機能です。
正式には「Language Integrated Query(言語統合クエリ)」の略で、データへの問い合わせ(検索・並べ替え・集計など)を自然な形で書けるようにする仕組みです。
LINQが登場する前までは、ループやif文を使って複雑な処理を書く必要がありましたが、LINQを使えば短くて読みやすいコードになります。
LINQの基本構文とラムダ式
LINQを使うと、リストの中から条件に合うデータを簡単に取り出したり、並び替えたりできます。
たとえば、次のように書くと「偶数(2で割り切れる数値)だけを取り出す」ことができます(Where
については後ほど説明します)。
var numbers = new List<int> { 1, 2, 3 };
var evenNumbers = numbers.Where(x => x % 2 == 0);
このコードでは、numbers
というリストにある 1、2、3 のそれぞれが、順番に x
に入っていきます。そして x % 2 == 0
という条件を満たす値(偶数)だけが取り出されます。
この x => x % 2 == 0
のような書き方をラムダ式と呼びます。
ラムダ式は、「関数(処理のかたまり)」をその場で簡潔に書くための記法です。
たとえば次の式は、「xという値を受け取って、1を足して返す関数」となります。
もしx
が1だったら2を、x
が2だったら3を返すという動きになります。
x => x + 1
ここでの x
は、リストの中から1つずつ取り出された要素を表します。
リストの中に「りんご」「バナナ」「みかん」が入っていれば、x
には順番に「りんご」「バナナ」「みかん」が入るというイメージです。
x という変数名は自分で自由に決めることができます(たとえば n や item
などでも構いません)。
どの名前を使っても、リストから取り出された1つ1つの要素が順番にその変数に入っていく、という仕組みは変わりません。
LINQではこのラムダ式を使って、「どんな条件で選ぶか」や「どのように変換するか」を指定します。
最初は見慣れないかもしれませんが、実際に使いながら覚えていけば大丈夫です。
よく使うLINQメソッド
ここでは、実務でもよく使われるLINQの代表的な機能を紹介します。
絞り込み(Where)
特定の条件に該当する要素だけを取り出したいときに使います。
var numbers = new List<int> { 100, 150, 80, 120, 500, 300 };
var filtered = numbers.Where(n => n > 100);
この例では、100より大きい数だけを取り出しています。Where
の中では、「nが100より大きいかどうか」という条件が書かれています。
並べ替え(OrderBy / OrderByDescending)
リストの要素を小さい順(昇順)や大きい順(降順)に並べたいときに使います。
var numbers = new List<int> { 100, 150, 80, 120, 500, 300 };
// 昇順(小さい順)
var ascending = numbers.OrderBy(n => n);
// 降順(大きい順)
var descending = numbers.OrderByDescending(n => n);
このように、OrderBy
を使えば数値や文字列を「昇順(小さい順、あいうえお順)」に並び替えられます。
逆に、OrderByDescending
を使えば「降順(大きい順、逆のあいうえお順)」で並べ替えることができます。
集計(Count, Sum, Average, Min, Max)
要素の数を数えたり、合計や平均、最大・最小の値を計算したいときに使います。
var numbers = new List<int> { 100, 150, 80, 120, 500, 300 };
// 条件(100より大きい)に合致する要素の件数を数える
var countOver100 = numbers.Count(n => n > 100);
// 合計を計算する
var total = numbers.Sum();
// 平均を計算する
var avg = numbers.Average();
// 最大値を取得する
var max = numbers.Max();
// 最小値を取得する
var min = numbers.Min();
このようにLINQを使うと、手動でループして計算する必要が無くなるので、コードがとてもシンプルになります。
変換(Select)
コレクションの各要素から、必要な部分だけを取り出したり、別の形に変換したいときに使います。
var words = new List<string> { "apple", "banana", "carrot" };
// すべての文字列を大文字に変換
var upperWords = words.Select(w => w.ToUpper()); // -> "APPLE", "BANANA", "CARROT"
この例では、英小文字で書かれた単語(”apple”など)を、すべて英大文字(”APPLE”)に変換しています。
条件チェック(Any / All)
「特定の条件を満たすデータがあるか」「すべてのデータが条件を満たしているか」を確認したいときに使います。
var numbers = new List<int> { 100, 150, 80, 120, 500, 300 };
var hasLarge = numbers.Any(n => n > 200); // true
var allUnder1000 = numbers.All(n => n < 400); // false
Any
は、1つでも条件に合う要素があればtrueを返します(この例では200より大きい数があるのでtrue)。
All
は、全ての要素が条件を満たしていればtrueを返します(この例では全ての数が400未満ではないのでfalse)。
先頭取得(First / FirstOrDefault)
条件に合う最初の要素を取り出したいときに使います。
var names = new List<string> { "りんご", "バナナ", "にんじん", "トマト", "牛肉", "鶏肉" };
// 条件に合う最初の要素を取得(条件に合う要素が1件もなかった場合は例外(エラー)が発生)
var firstMatch = names.First(n => n.Length > 3);
// 条件に合う最初の要素を取得(条件に合う要素が1件もなかった場合はnullを返す)
var firstMatchOrNull = names.FirstOrDefault(n => n.Length > 10); // 該当なし → null
この例では、「文字数が3文字より多い最初の要素(”にんじん”)」を取得します。
First()
は、条件に合う要素が1件もなかった場合に例外(エラー)が発生します。
要素が存在しない可能性があり、例外を発生させたくない場合は、FirstOrDefault()
を使うと安全です。
その他の便利なLINQメソッド
LINQには他にも便利なメソッドがいくつか用意されています。
重複の除外(Distinct)
同じ値がリストの中に何度も出てくる場合、それらの重複を取り除くのが Distinct()
です。
var names = new List<string> { "りんご", "バナナ", "バナナ", "トマト", "牛肉", "トマト" };
// 同じ文字列を1つにまとめる
var uniqueNames = names.Distinct();
この例では「バナナ」や「トマト」が2回ずつ登場していますが、Distinct()
を使うと重複のないリストが得られます。
一部だけ取り出す(Skip / Take)
リストの中から、「先頭から何件かを飛ばして、一部だけを取り出したい」というときに使います。
例えば、一覧データをページごとに表示したい場合などに便利です。
var items = new List<string> { "A", "B", "C", "D", "E", "F" };
// 先頭から2件をスキップし、次の3件を取得
var result = items.Skip(2).Take(3); // 結果: "C", "D", "E"
この例では、まず .Skip(2)
によって「A」「B」をスキップし、その後 .Take(3)
で「C」「D」「E」の3件を取り出しています。
Skip(n)
:先頭から n 件を飛ばすTake(m)
:m 件を取り出す
組み合わせることで、「nページ目の表示データ」のような処理が簡単に実現できます。
リストへの変換(ToList)
LINQのメソッドを使った結果は、多くの場合List<T>
型ではなく、IEnumerable<T>
型になります。
これは「すぐには結果を確定せず、必要になったときに中身を取り出す」という遅延評価の仕組みによるものです。(遅延評価については次のセクションで解説)
LINQの結果をリストとして取得したい場合には、ToList()
を使います。
var numbers = new List<int> { 100, 150, 80, 120, 500, 300 };
// 条件に合う数値だけを取り出してリストに変換
var filteredList = numbers.Where(n => n > 200).ToList();
ToList()
を使うとLINQの結果がList<T>
型に変換されます。
これにより、あとから件数を数えたり、インデックスでアクセスしたりといった操作が可能になります。
LINQの注意点とベストプラクティス
LINQはとても便利ですが、使い方を少し間違えるとパフォーマンスが落ちたり、コードが読みにくくなったりすることもあります。
ここでは、よくある注意点と、それを防ぐための工夫を紹介します。
遅延評価と即時評価
LINQには「遅延評価」という特徴があります。これは、LINQでの処理を記述しても、その時点では実行されないという仕組みです。
実際に結果を使おうとしたとき(たとえば foreach
で回したときなど)に、初めて処理が実行されます。
var numbers = new List<int> { 100, 200, 300 };
// この時点ではまだ Where は実行されていない
var filtered = numbers.Where(n => n > 150);
// ここで初めて Where が実行され、条件に合う要素が取り出される
foreach (var number in filtered)
{
Console.WriteLine(number); // 出力: 200, 300
}
このとき、 filtered
は「200, 300を保持している」のではなく、毎回「n > 150」の条件を使って元の numbers
をチェックしています。
つまり、foreach
を回すたびに、numbers
を最初から走査して条件を判定し直している、というわけです。
遅延評価には、必要なときだけ実行される・無駄なメモリを使わないといったメリットがあります。
一方で、結果を何度も使いたい場合や元のリストが変更される可能性がある場合は、処理が毎回再実行されるとパフォーマンス面やデータの整合性に問題が出ることがあります。
そういった場面では、.ToList()
などを使って結果を確定(即時評価)しておくと安全です。
var numbers = new List<int> { 100, 200, 300 };
// ToList() を使うと、Where の結果がこの時点で確定する
var filteredList = numbers.Where(n => n > 150).ToList();
// filteredList は List<int> なので、繰り返し使っても再計算されない
Console.WriteLine(filteredList.Count); // 件数の取得も高速
Console.WriteLine(filteredList[0]); // インデックスでアクセスも可能
状況に応じて「遅延評価のまま使う」か「ToListなどで確定させる」かを適切に使い分けることが、LINQを使いこなすうえで重要になります。
メソッドチェーンと可読性
LINQでは、メソッドを繋げて(チェーンして)複数書くことができます。
処理がシンプルなときはこれでOKですが、繋げすぎると読みづらくなるので注意が必要です。
// 長くなりすぎたチェーンの例(読みづらい)
var result = list.Where(x => x > 0).Select(x => x * 2).OrderByDescending(x => x).Take(3);
こうした場合は、メソッドごとに改行して書くか、途中で変数に分けることで可読性を高めることができます。
// 改行して読みやすく書いた例
var result = list
.Where(x => x > 0)
.Select(x => x * 2)
.OrderByDescending(x => x)
.Take(3);
さらに処理が複雑になる場合は、途中の結果を変数に代入して段階的に書くのもおすすめです。
var positives = list.Where(x => x > 0);
var doubled = positives.Select(x => x * 2);
var result = doubled.OrderByDescending(x => x).Take(3);
このように「読みやすさ」と「簡潔さ」のバランスを意識して書くことが、LINQを上手に使うコツです。
まとめ
LINQは非常に便利で実務でも活用の機会が多い機能です。
初めのうちはよく使うメソッドだけでも十分なので、少しずつ慣れて習得していきましょう。
次回は、クラスとオブジェクト指向の基本に解説します。