カテゴリ: C# 更新日: 2026/02/08

C#のLINQ活用ベストプラクティス!初心者向けまとめ

C#のLINQ活用ベストプラクティス!初心者向けまとめ
C#のLINQ活用ベストプラクティス!初心者向けまとめ

先生と生徒の会話形式で理解しよう

生徒

「先生、LINQを使い始めたんですが、どうやって書くのが良いのか迷ってしまいます。」

先生

「LINQは便利ですが、使い方によってはコードが読みにくくなったり、パフォーマンスが悪くなったりすることがあります。」

生徒

「どんなことに気をつければいいんですか?」

先生

「それでは、LINQを効果的に使うためのベストプラクティスを見ていきましょう!」

1. LINQとは?

1. LINQとは?
1. LINQとは?

LINQは「Language Integrated Query(ランゲージ・インテグレーテッド・クエリ)」の略で、C#の中でデータを検索したり、並び替えたり、フィルタリングしたりする機能です。例えば、たくさんの商品リストから「値段が千円以下の商品だけを取り出す」といった操作を、簡潔に書くことができます。データベースのSQLに似た書き方で、配列やリスト、コレクションといったデータをスマートに操作できるのが特徴です。

2. クエリ構文とメソッド構文の使い分け

2. クエリ構文とメソッド構文の使い分け
2. クエリ構文とメソッド構文の使い分け

LINQには「クエリ構文」と「メソッド構文」という二つの書き方があります。クエリ構文はSQLのような書き方で、メソッド構文はメソッドを繋げて書く方法です。どちらを使っても同じ結果が得られますが、使い分けることで読みやすいコードになります。

クエリ構文の例


int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var evenNumbers = from n in numbers
                  where n % 2 == 0
                  select n;

foreach (var num in evenNumbers)
{
    Console.WriteLine(num);
}

メソッド構文の例


int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

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

foreach (var num in evenNumbers)
{
    Console.WriteLine(num);
}

基本的にはメソッド構文の方が柔軟性が高く、複雑な処理を書きやすいため、メソッド構文を使うことが推奨されます。ただし、複数のデータソースを結合する場合など、クエリ構文の方が読みやすい場合もあります。

3. 遅延実行を理解する

3. 遅延実行を理解する
3. 遅延実行を理解する

LINQの重要な特徴の一つが「遅延実行」です。遅延実行とは、LINQクエリを書いた時点では実際の処理が実行されず、結果を使おうとした時に初めて実行される仕組みのことです。これはパフォーマンスの最適化に役立ちますが、理解していないと予想外の動作をすることがあります。


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

// この時点ではまだ実行されていない
var query = numbers.Where(n => n > 2);

// リストに要素を追加
numbers.Add(6);

// foreachで使う時に初めて実行される
foreach (var num in query)
{
    Console.WriteLine(num);  // 3, 4, 5, 6 が出力される
}

このコードでは、クエリを作成した後に数値を追加していますが、foreachで使う時に実行されるため、追加した6も結果に含まれます。もし実行時点でのデータを確定させたい場合は、ToList()やToArray()を使って即座に実行します。


var query = numbers.Where(n => n > 2).ToList();

4. 不必要なToList()やToArray()を避ける

4. 不必要なToList()やToArray()を避ける
4. 不必要なToList()やToArray()を避ける

初心者がよくやってしまうのが、必要ないのにToList()やToArray()を呼んでしまうことです。これらのメソッドは遅延実行を即座に実行してメモリに結果を格納するため、不必要に使うとパフォーマンスが悪化します。

悪い例


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

良い例


var result = numbers.Where(n => n > 5).First();

悪い例では、全ての条件に合う要素をリストに変換してから最初の要素を取得していますが、良い例では条件に合う最初の要素が見つかった時点で処理が終わります。結果を繰り返し使う場合や、後で変更する場合のみToList()を使いましょう。

5. Any()とCount()の使い分け

5. Any()とCount()の使い分け
5. Any()とCount()の使い分け

データが存在するかどうかを確認する時、Count()を使うのは効率的ではありません。Any()メソッドを使うと、最初の要素が見つかった時点で処理が終わるため、パフォーマンスが向上します。

悪い例


if (numbers.Count() > 0)
{
    Console.WriteLine("データがあります");
}

良い例


if (numbers.Any())
{
    Console.WriteLine("データがあります");
}

Count()は全ての要素を数えますが、Any()は一つでも要素があればすぐにtrueを返すため高速です。同様に、特定の条件に合うデータがあるか確認する場合もAny()を使います。


// 10より大きい数字があるかチェック
if (numbers.Any(n => n > 10))
{
    Console.WriteLine("10より大きい数字があります");
}

6. FirstOrDefault()とSingleOrDefault()の違い

6. FirstOrDefault()とSingleOrDefault()の違い
6. FirstOrDefault()とSingleOrDefault()の違い

データを取得する時、First()、FirstOrDefault()、Single()、SingleOrDefault()などのメソッドがありますが、それぞれ用途が異なります。適切に使い分けることで、バグを防ぎ、意図を明確に伝えられます。

  • First(): 最初の要素を取得。要素がない場合は例外が発生
  • FirstOrDefault(): 最初の要素を取得。要素がない場合はデフォルト値を返す
  • Single(): 唯一の要素を取得。要素が0個または2個以上の場合は例外が発生
  • SingleOrDefault(): 唯一の要素を取得。要素が0個の場合はデフォルト値、2個以上の場合は例外が発生

List<string> names = new List<string> { "田中", "佐藤", "鈴木" };

// 最初の名前を取得
var firstName = names.First();  // "田中"

// 条件に合う最初の名前を取得(なければnull)
var name = names.FirstOrDefault(n => n == "山田");  // null

// IDで検索する場合など、唯一であるべき時
var uniqueName = names.Single(n => n == "佐藤");  // "佐藤"

データベースからIDで検索する場合など、結果が必ず一つだけであるべき時はSingle()を使うと、複数のデータが返ってきた場合にエラーで気づくことができます。

7. 複数の条件を効率的に組み合わせる

7. 複数の条件を効率的に組み合わせる
7. 複数の条件を効率的に組み合わせる

複数のWhere()を繋げて書くこともできますが、一つのWhere()の中で条件を組み合わせる方が効率的な場合があります。ただし、可読性とのバランスを考えて使い分けましょう。

複数のWhereを繋げる


var result = numbers.Where(n => n > 5)
                    .Where(n => n < 20)
                    .Where(n => n % 2 == 0);

一つのWhereにまとめる


var result = numbers.Where(n => n > 5 && n < 20 && n % 2 == 0);

シンプルな条件であれば一つにまとめた方が効率的ですが、条件が複雑で長くなる場合は、分けて書いた方が読みやすくなることもあります。チームのコーディング規約に従って統一しましょう。

8. SelectとSelectManyの使い分け

8. SelectとSelectManyの使い分け
8. SelectとSelectManyの使い分け

Select()は各要素を変換するメソッドですが、SelectMany()は入れ子になったコレクションを平坦化するメソッドです。これを理解すると、複雑なデータ構造を扱えるようになります。


List<List<int>> nestedList = new List<List<int>>
{
    new List<int> { 1, 2, 3 },
    new List<int> { 4, 5, 6 },
    new List<int> { 7, 8, 9 }
};

// Selectを使うと List<List<int>> のまま
var selectResult = nestedList.Select(list => list);

// SelectManyを使うと平坦化されて List<int> になる
var selectManyResult = nestedList.SelectMany(list => list);

foreach (var num in selectManyResult)
{
    Console.WriteLine(num);  // 1から9まで順番に出力
}

SelectMany()は、複数のコレクションを一つにまとめたい時や、オブジェクトの中にあるリストの全要素を取得したい時に便利です。

9. null安全なLINQクエリを書く

9. null安全なLINQクエリを書く
9. null安全なLINQクエリを書く

LINQを使う時、nullチェックを忘れるとエラーが発生します。特にデータベースから取得したデータやユーザー入力を扱う時は、null安全なコードを書くことが重要です。


List<string> names = null;

// 悪い例:nullの場合エラーになる
// var result = names.Where(n => n.Length > 3);

// 良い例:nullチェックを追加
var result = names?.Where(n => n.Length > 3) ?? Enumerable.Empty<string>();

// または事前にチェック
if (names != null)
{
    var safeResult = names.Where(n => n.Length > 3);
}

null条件演算子(?.)とnull合体演算子(??)を組み合わせることで、安全にLINQクエリを書くことができます。Enumerable.Empty()は空のコレクションを返すメソッドで、nullではなく空のコレクションを扱うことでエラーを防ぎます。

10. メソッドチェーンの可読性を保つ

10. メソッドチェーンの可読性を保つ
10. メソッドチェーンの可読性を保つ

LINQは複数のメソッドを繋げて書くことができますが、長くなりすぎると読みにくくなります。適切に改行を入れて、可読性を保つことが大切です。

読みにくい例


var result = numbers.Where(n => n > 0).Select(n => n * 2).OrderBy(n => n).Take(10).ToList();

読みやすい例


var result = numbers
    .Where(n => n > 0)
    .Select(n => n * 2)
    .OrderBy(n => n)
    .Take(10)
    .ToList();

各メソッドを改行して書くことで、処理の流れが一目で分かるようになります。特に複雑なクエリを書く時は、この書き方を心がけましょう。

11. パフォーマンスを意識したOrderByの使い方

11. パフォーマンスを意識したOrderByの使い方
11. パフォーマンスを意識したOrderByの使い方

OrderBy()やOrderByDescending()でデータを並び替える時、パフォーマンスに注意が必要です。特に大量のデータを扱う場合、並び替えは処理時間がかかる操作です。


// 必要な件数だけ取得してから並び替える
var result = numbers
    .Where(n => n > 100)
    .Take(10)
    .OrderBy(n => n);

// より効率的:先に並び替えてから必要な件数を取得
var betterResult = numbers
    .Where(n => n > 100)
    .OrderBy(n => n)
    .Take(10);

どちらの順序で書くかによって、パフォーマンスが変わることがあります。一般的には、フィルタリングで件数を減らしてから並び替える方が効率的ですが、Take()を使う場合は並び替えてから取得する方が正確な結果が得られます。

カテゴリの一覧へ
新着記事
New1
C#
C#で条件分岐をネストする方法!入れ子構造の書き方を基礎から学ぼう
New2
C#
C#のnull合体演算子(??)の使い方!デフォルト値を設定する便利技
New3
C#
C#のLINQ活用ベストプラクティス!初心者向けまとめ
New4
C#
C#のコーディング規約(C# Coding Conventions)とは?読みやすいコードを書くための基本ルール
人気記事
No.1
Java&Spring記事人気No1
C#
C#のLINQでOrderByを使った並び替えを完全ガイド!初心者でもわかるソート方法
No.2
Java&Spring記事人気No2
C#
C#のpartialクラスとは?初心者でも理解できるクラス分割の基本
No.3
Java&Spring記事人気No3
COBOL
COBOLの数値データ型「PIC 9」の使い方と注意点をやさしく解説!
No.4
Java&Spring記事人気No4
C#
C#で文字列が数値か判定する方法を解説!char.IsDigitやTryParseの基本
No.5
Java&Spring記事人気No5
C#
C#の引数と戻り値の基本!値を受け渡し・返す仕組みを理解しよう
No.6
Java&Spring記事人気No6
COBOL
COBOLの帳票レイアウトを美しく!可読性向上テクニックを徹底解説
No.7
Java&Spring記事人気No7
C#
C#で型を調べる方法!GetType()・typeof演算子の違いと使い方
No.8
Java&Spring記事人気No8
COBOL
COBOLのコンパイラと開発環境を徹底解説!初心者にもわかりやすい入門ガイド