C# LINQとは?初心者がつまずく使い方・書き方・実践例をわかりやすく解説

はじめに

C#でデータを扱うとき、よく登場するのが「LINQ」です。

LINQを使うと、Listや配列などのコレクションから条件に合うデータを取り出したり、並べ替えたり、集計したりする処理を短く読みやすく書けます。

たとえば、ユーザー一覧から「20歳以上のユーザーだけを取得する」「商品一覧を価格が安い順に並べる」「売上データをカテゴリ別に合計する」といった処理は、C# LINQが得意とする代表的な使い方です。

一方で、初心者にとってはラムダ式、Where、Select、遅延実行、IEnumerable、IQueryableなど、最初につまずきやすいポイントも多くあります。

この記事では、C# LINQとは何かという基本から、よく使うメソッド、実践例、初心者が間違えやすいポイント、実務での書き方のコツまで、順番にわかりやすく解説します。

1. C# LINQとは?初心者にもわかる基本概念

LINQは、C#でデータを扱うための便利な機能です。

List、配列、Dictionary、データベース、XMLなど、さまざまなデータに対して「検索」「抽出」「並べ替え」「集計」「変換」といった処理を統一的な書き方で行えます。

初心者のうちは、LINQを「データの一覧をわかりやすく加工するための書き方」と考えると理解しやすいです。

1-1. LINQの読み方と正式名称

LINQは「リンク」と読みます。

正式名称は「Language Integrated Query」です。日本語にすると「言語に統合された問い合わせ」という意味です。

「問い合わせ」と聞くとデータベースを連想するかもしれませんが、LINQはデータベースだけでなく、C#のListや配列にも使えます。

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

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

この例では、numbersの中から偶数だけを取り出しています。

1-2. LINQでできること:検索・抽出・並べ替え・集計・変換

LINQでは、主に次のような処理ができます。

データを条件で絞り込む場合はWhereを使います。

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

必要な値だけを取り出す場合はSelectを使います。

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

並べ替える場合はOrderByやOrderByDescendingを使います。

C#
var sortedProducts = products.OrderBy(product => product.Price);

合計や平均を求める場合はSumやAverageを使います。

C#
var total = sales.Sum(sale => sale.Amount);

このように、LINQを使うと「何をしたいのか」がコードから読み取りやすくなります。

1-3. foreachやif文で書く処理とLINQの違い

LINQを使わずに、Listから条件に合うデータを取り出す場合は、foreachとif文を使って次のように書けます。

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

foreach (var number in numbers)
{
if (number >= 3)
{
result.Add(number);
}
}

同じ処理をLINQで書くと、次のようになります。

C#
var result = numbers.Where(number => number >= 3).ToList();

foreachを使う書き方は処理の流れが明確です。一方、LINQは短く書けて、「numbersから3以上の値を取り出す」という意図が伝わりやすくなります。

ただし、LINQを使えば必ず良いわけではありません。処理が複雑すぎる場合は、foreachで書いたほうが読みやすいこともあります。

1-4. LINQが使われる主なデータ:List・配列・Dictionary・DB・XML

C# LINQは、さまざまなデータに対して使えます。

Listに対して使う例です。

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

var result = names.Where(name => name.Length >= 5);

配列に対しても使えます。

C#
int[] scores = { 80, 95, 60, 70 };

var highScores = scores.Where(score => score >= 80);

Dictionaryにも使えます。

C#
var prices = new Dictionary<string, int>
{
{ "Apple", 120 },
{ "Banana", 80 },
{ "Orange", 150 }
};

var expensiveItems = prices.Where(item => item.Value >= 100);

また、Entity Frameworkを使うと、データベースに対してもLINQを書けます。

C#
var activeUsers = db.Users.Where(user => user.IsActive);

このように、データの種類が変わっても似た書き方で処理できるのがLINQの大きな特徴です。

1-5. C#初心者がLINQを学ぶメリット

C#初心者がLINQを学ぶメリットは大きく分けて3つあります。

1つ目は、コードが短くなることです。foreachとif文で何行も書く処理を、LINQなら1行から数行で表現できます。

2つ目は、意図が伝わりやすくなることです。Whereなら絞り込み、Selectなら変換、OrderByなら並べ替えというように、メソッド名から処理内容がわかります。

3つ目は、実務でよく使われることです。C#の業務アプリ、Webアプリ、API開発、データベース操作ではLINQが頻繁に登場します。

特に、Listや配列を扱う処理が多い場合、LINQを理解しているとコードの読み書きがかなり楽になります。

2. LINQを使う前に押さえたい前提知識

LINQを理解するには、いくつかの前提知識があります。

最初からすべてを完璧に理解する必要はありませんが、コレクション、IEnumerable、IQueryable、ラムダ式、Func、using System.Linqの意味を知っておくと、LINQの理解がスムーズになります。

2-1. コレクションとは何か

コレクションとは、複数のデータをまとめて扱うための入れ物です。

代表的なコレクションには、List、配列、Dictionaryがあります。

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

このnumbersは、int型の値を複数持つコレクションです。

LINQは、このようなコレクションに対して「条件に合うものを探す」「必要な形に変換する」「並べ替える」といった処理を行うために使います。

2-2. IEnumerableとIQueryableの違い

LINQを学ぶと、IEnumerableとIQueryableという言葉が出てきます。

IEnumerableは、主にメモリ上のデータを順番に取り出すための仕組みです。Listや配列に対してLINQを使うと、多くの場合IEnumerableとして扱われます。

C#
IEnumerable<int> result = numbers.Where(n => n >= 3);

IQueryableは、主にデータベースなど外部のデータソースに対して使われます。Entity Frameworkでデータベース検索を行う場合によく登場します。

C#
IQueryable<User> users = db.Users.Where(user => user.IsActive);

大まかに言うと、IEnumerableは「C#の中で処理する」、IQueryableは「SQLなどに変換して外部で処理する」イメージです。

2-3. ラムダ式の基本

LINQでは、ラムダ式をよく使います。

ラムダ式は、簡単に言うと「短く書ける関数」です。

C#
n => n >= 3

これは、「nを受け取って、nが3以上かどうかを返す」という意味です。

Whereで使うと、次のようになります。

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

ここでのnは、numbersの中から1つずつ取り出される値です。

文字列のListであれば、次のように書けます。

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

var result = names.Where(name => name.Length >= 5);

この場合、nameはnamesの中の1つひとつの文字列を表します。

2-4. デリゲートとFuncの考え方

LINQでは、内部的にFuncという型がよく使われます。

Funcは「処理を変数のように渡すための型」です。

たとえば、次のように書けます。

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

var result = numbers.Where(isEven);

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

Whereは、各要素に対してtrueかfalseを返す処理を受け取り、trueになった要素だけを残します。

初心者のうちは、Funcやデリゲートを深く理解していなくてもLINQは使えます。ただし、「ラムダ式は処理を渡している」と考えると、WhereやSelectの意味がわかりやすくなります。

2-5. using System.Linqが必要な理由

LINQのWhere、Select、OrderByなどを使うには、通常次のusingが必要です。

C#
using System.Linq;

System.Linqには、LINQで使う拡張メソッドが定義されています。

もしWhereやSelectを書いたときに「定義がありません」というエラーが出た場合、using System.Linqが不足している可能性があります。

C#
using System;
using System.Collections.Generic;
using System.Linq;

最近のC#プロジェクトでは、暗黙的にusingされている場合もありますが、LINQが使えないときはまずSystem.Linqを確認しましょう。

3. LINQの基本的な書き方

LINQには大きく分けて2つの書き方があります。

1つはメソッド構文、もう1つはクエリ構文です。

実務ではメソッド構文がよく使われますが、SQLに近い見た目で書けるクエリ構文もあります。

3-1. メソッド構文とは

メソッド構文は、WhereやSelectなどのメソッドをつなげて書く方法です。

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

このコードは、usersから20歳以上のユーザーを絞り込み、その名前だけを取り出してListに変換しています。

メソッド構文は、C# LINQで最もよく使われる書き方です。

3-2. クエリ構文とは

クエリ構文は、SQLに似た形でLINQを書く方法です。

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

from、where、selectといったキーワードを使って書くため、SQLに慣れている人には読みやすい場合があります。

ただし、すべてのLINQメソッドをクエリ構文だけで表現できるわけではありません。実務ではメソッド構文と組み合わせて使うこともあります。

3-3. メソッド構文とクエリ構文の違い

メソッド構文とクエリ構文は、書き方が違うだけで、同じような処理を表現できます。

メソッド構文の例です。

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

クエリ構文の例です。

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

どちらも「20歳以上のユーザーの名前を取得する」という処理です。

メソッド構文は短く、メソッドチェーンで柔軟に書けます。クエリ構文は、複雑な結合やSQLに近い処理で読みやすくなることがあります。

3-4. 初心者はどちらの書き方から覚えるべきか

初心者には、まずメソッド構文から覚えることをおすすめします。

理由は、Where、Select、OrderBy、GroupBy、Any、FirstOrDefaultなど、実務でよく使うLINQメソッドの理解につながりやすいからです。

また、インターネット上のC# LINQのサンプルコードもメソッド構文が多く、学習しやすいです。

まずはメソッド構文で基本メソッドを覚え、必要に応じてクエリ構文も読めるようにしていくとよいでしょう。

3-5. LINQの基本パターン:Where・Select・OrderBy

LINQの基本は、Where、Select、OrderByです。

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

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

Selectは必要な形に変換します。

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

OrderByは昇順で並べ替えます。

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

これらを組み合わせると、実務でよく使う処理を書けます。

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

このコードは、「20歳以上のユーザーを年齢順に並べ、名前だけをListにする」という意味です。

4. よく使うLINQメソッド一覧と使い方

ここからは、C# LINQでよく使うメソッドを具体例つきで解説します。

すべてを一度に覚える必要はありません。まずはWhere、Select、OrderBy、FirstOrDefault、Any、ToListあたりから覚えると実践で使いやすいです。

4-1. Where:条件に合う要素を抽出する

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

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

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

結果は3、4、5です。

オブジェクトのListにも使えます。

C#
var activeUsers = users.Where(user => user.IsActive);

Whereのラムダ式は、trueまたはfalseを返します。trueになった要素だけが結果に残ります。

4-2. Select:必要な値だけ取り出す・変換する

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

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

この例では、ユーザー一覧から名前だけを取り出しています。

匿名型に変換することもできます。

C#
var result = users.Select(user => new
{
user.Name,
user.Age
});

Selectは「絞り込み」ではなく「変換」です。条件でデータを減らしたい場合はWhere、形を変えたい場合はSelectを使います。

4-3. OrderBy・OrderByDescending:昇順・降順で並べ替える

OrderByは昇順、OrderByDescendingは降順で並べ替えるメソッドです。

C#
var sorted = products.OrderBy(product => product.Price);

価格が安い順に並びます。

C#
var sortedDesc = products.OrderByDescending(product => product.Price);

価格が高い順に並びます。

複数条件で並べ替えたい場合はThenByを使います。

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

この例では、年齢順に並べたあと、同じ年齢の中で名前順に並べます。

4-4. First・FirstOrDefault:最初の要素を取得する

Firstは、条件に合う最初の要素を取得します。

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

ただし、条件に合う要素が1件もない場合、Firstは例外を発生させます。

安全に取得したい場合はFirstOrDefaultを使います。

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

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

初心者は、まずFirstOrDefaultを使う場面を多めに考えるとエラーを防ぎやすいです。

4-5. Single・SingleOrDefault:1件だけ取得する

Singleは、条件に合う要素が「必ず1件だけ」であることを期待するメソッドです。

C#
var user = users.Single(user => user.Id == 1);

条件に合う要素が0件でも、2件以上でも例外になります。

SingleOrDefaultは、0件の場合はdefaultを返しますが、2件以上ある場合は例外になります。

C#
var user = users.SingleOrDefault(user => user.Id == 1);

Singleは、「この条件では絶対に1件だけのはず」という意図を表すときに使います。単に最初の1件がほしいだけならFirstOrDefaultを使うほうが適切です。

4-6. Any・All:条件に合う要素があるか判定する

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

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

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

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

存在チェックにはCountよりAnyを使うのが基本です。

C#
if (users.Any())
{
Console.WriteLine("ユーザーが存在します");
}

4-7. Count・Sum・Average・Max・Min:件数や合計を集計する

Countは件数を数えます。

C#
int count = users.Count();

条件に合う件数も数えられます。

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

Sumは合計を求めます。

C#
int total = orders.Sum(order => order.Amount);

Averageは平均を求めます。

C#
double average = scores.Average();

MaxとMinは最大値・最小値を取得します。

C#
int maxScore = scores.Max();
int minScore = scores.Min();

集計メソッドは、売上、点数、数量、価格などの処理でよく使います。

4-8. GroupBy:グループ化する

GroupByは、指定したキーごとにデータをグループ化します。

C#
var groups = products.GroupBy(product => product.Category);

カテゴリごとに商品をまとめる例です。

C#
foreach (var group in groups)
{
Console.WriteLine(group.Key);

foreach (var product in group)
{
Console.WriteLine(product.Name);
}
}

集計と組み合わせると、カテゴリ別の合計も求められます。

C#
var result = sales
.GroupBy(sale => sale.Category)
.Select(group => new
{
Category = group.Key,
Total = group.Sum(sale => sale.Amount)
});

4-9. Distinct:重複を取り除く

Distinctは、重複した値を取り除きます。

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

var result = numbers.Distinct();

結果は1、2、3です。

文字列にも使えます。

C#
var uniqueNames = names.Distinct();

オブジェクトの重複を取り除く場合は、比較の基準に注意が必要です。特定のプロパティで重複を除きたい場合は、DistinctByを使える環境であれば次のように書けます。

C#
var uniqueUsers = users.DistinctBy(user => user.Email);

4-10. Take・Skip:指定件数だけ取得・スキップする

Takeは、先頭から指定件数だけ取得します。

C#
var top3 = products
.OrderByDescending(product => product.Price)
.Take(3);

この例では、価格が高い商品を上位3件取得しています。

Skipは、指定件数をスキップします。

C#
var result = products.Skip(10).Take(10);

これは、11件目から20件目までを取得するようなページング処理でよく使われます。

4-11. ToList・ToArray・ToDictionary:結果をコレクションに変換する

LINQの結果は、そのままだとIEnumerableとして返ることが多いです。

Listとして使いたい場合はToListを使います。

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

配列にしたい場合はToArrayです。

C#
var array = users.ToArray();

Dictionaryにしたい場合はToDictionaryを使います。

C#
var dictionary = users.ToDictionary(user => user.Id);

キーと値を指定することもできます。

C#
var dictionary = users.ToDictionary(
user => user.Id,
user => user.Name
);

ToDictionaryでは、キーが重複すると例外になるため注意しましょう。

5. 実践例で学ぶC# LINQの使い方

ここからは、実際の開発でよくあるケースをもとに、C# LINQの使い方を見ていきます。

サンプルでは、次のようなクラスを使います。

C#
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
public bool IsActive { get; set; }
}
C#
public class Product
{
public string Name { get; set; } = "";
public string Category { get; set; } = "";
public int Price { get; set; }
}

5-1. Listから条件に合うデータを検索する

ユーザー一覧から20歳以上のユーザーだけを取得する例です。

C#
var users = new List<User>
{
new User { Id = 1, Name = "Alice", Age = 25, IsActive = true },
new User { Id = 2, Name = "Bob", Age = 17, IsActive = true },
new User { Id = 3, Name = "Charlie", Age = 30, IsActive = false }
};

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

Whereを使うことで、条件に合うデータだけを簡単に抽出できます。

5-2. 商品一覧から価格順に並べ替える

商品一覧を価格が安い順に並べる例です。

C#
var products = new List<Product>
{
new Product { Name = "Mouse", Category = "PC", Price = 2000 },
new Product { Name = "Keyboard", Category = "PC", Price = 5000 },
new Product { Name = "Monitor", Category = "PC", Price = 30000 }
};

var sortedProducts = products
.OrderBy(product => product.Price)
.ToList();

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

C#
var expensiveFirst = products
.OrderByDescending(product => product.Price)
.ToList();

5-3. ユーザー一覧から名前だけを取り出す

Userオブジェクトの一覧からNameだけを取り出す例です。

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

Selectは、データの形を変えるときに使います。

画面表示用に必要な項目だけ取り出す場合にも便利です。

C#
var displayUsers = users
.Select(user => new
{
user.Id,
DisplayName = user.Name
})
.ToList();

5-4. 売上データをカテゴリ別に集計する

カテゴリ別に売上合計を求める例です。

C#
public class Sale
{
public string Category { get; set; } = "";
public int Amount { get; set; }
}
C#
var sales = new List<Sale>
{
new Sale { Category = "Book", Amount = 1200 },
new Sale { Category = "Book", Amount = 1800 },
new Sale { Category = "Food", Amount = 900 }
};

var result = sales
.GroupBy(sale => sale.Category)
.Select(group => new
{
Category = group.Key,
Total = group.Sum(sale => sale.Amount)
})
.ToList();

GroupByとSumを組み合わせると、集計処理をシンプルに書けます。

5-5. 重複したデータを削除する

数値や文字列の重複を削除する場合はDistinctを使います。

C#
var tags = new List<string>
{
"C#",
"LINQ",
"C#",
"ASP.NET",
"LINQ"
};

var uniqueTags = tags.Distinct().ToList();

結果は、C#、LINQ、ASP.NETです。

ユーザーのメールアドレスなど、特定の値で重複を判断したい場合はDistinctByが使えると便利です。

C#
var uniqueUsers = users
.DistinctBy(user => user.Name)
.ToList();

5-6. 複数条件で絞り込む

複数条件で絞り込む場合は、Whereの中で&&や||を使います。

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

これは、「20歳以上かつ有効なユーザー」を取得する例です。

または、Whereを複数つなげても同じように書けます。

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

条件が複雑な場合は、複数のWhereに分けると読みやすくなります。

5-7. Dictionaryに変換して検索しやすくする

ユーザーIDからユーザーをすばやく取得したい場合、ToDictionaryを使うと便利です。

C#
var userDictionary = users.ToDictionary(user => user.Id);

var user = userDictionary[1];

存在しないキーを指定すると例外になるため、安全に取得したい場合はTryGetValueを使います。

C#
if (userDictionary.TryGetValue(1, out var user))
{
Console.WriteLine(user.Name);
}

ToDictionaryは便利ですが、キーが重複すると例外になります。IDやメールアドレスなど、一意であることが保証されている値をキーにしましょう。

5-8. nullを含むデータを安全に扱う

Nameがnullの可能性がある場合、何も考えずにLengthを使うと例外になることがあります。

C#
var result = users
.Where(user => user.Name != null && user.Name.Length >= 5)
.ToList();

null条件演算子を使うこともできます。

C#
var result = users
.Where(user => user.Name?.Length >= 5)
.ToList();

Selectでnullを別の値に置き換える場合は、null合体演算子を使えます。

C#
var names = users
.Select(user => user.Name ?? "名前なし")
.ToList();

LINQでは短く書けるぶん、nullチェックを忘れないことが大切です。

6. 初心者がLINQでつまずきやすいポイント

C# LINQは便利ですが、初心者がつまずきやすいポイントもあります。

特に、ラムダ式の変数、WhereとSelectの違い、FirstとFirstOrDefaultの使い分け、遅延実行、null参照はよくある悩みです。

6-1. ラムダ式のxやpが何を表しているかわからない

LINQのサンプルでは、よくxやpといった変数名が使われます。

C#
var result = products.Where(p => p.Price >= 1000);

このpは、productsの中から1つずつ取り出されるProductを表しています。

つまり、次のforeachのproductと同じような意味です。

C#
foreach (var product in products)
{
if (product.Price >= 1000)
{
// 条件に合う
}
}

初心者のうちは、xやpではなく、意味のある名前を使うと理解しやすくなります。

C#
var result = products.Where(product => product.Price >= 1000);

6-2. SelectとWhereの違いがわからない

Whereはデータを絞り込むメソッドです。

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

Selectはデータを変換するメソッドです。

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

Whereは件数が減る可能性があります。Selectは件数は基本的に変わらず、要素の形が変わります。

「条件に合うものだけほしい」ならWhere、「必要な項目だけ取り出したい」ならSelectと覚えましょう。

6-3. FirstとFirstOrDefaultの使い分けでエラーになる

Firstは、該当する要素がない場合に例外を発生させます。

C#
var user = users.First(user => user.Id == 999);

Idが999のユーザーがいなければエラーになります。

安全に扱いたい場合はFirstOrDefaultを使います。

C#
var user = users.FirstOrDefault(user => user.Id == 999);

if (user == null)
{
Console.WriteLine("ユーザーが見つかりません");
}

「見つからない可能性がある検索」では、FirstOrDefaultを使うほうが安全です。

6-4. SingleとFirstの違いを誤解する

Firstは、条件に合う最初の1件を取得します。複数件あっても、最初の1件を返します。

Singleは、条件に合うデータが1件だけであることを確認します。複数件あると例外になります。

C#
var user = users.Single(user => user.Id == 1);

IDのように一意であるべき値で検索する場合はSingleを使うと、データの異常に気づけます。

ただし、単に「最初の1件がほしい」だけならFirstやFirstOrDefaultを使いましょう。

6-5. Count()とAny()の使い分けを間違える

データが存在するか確認するために、次のように書くことがあります。

C#
if (users.Count() > 0)
{
Console.WriteLine("ユーザーが存在します");
}

この場合は、Anyを使うほうが適切です。

C#
if (users.Any())
{
Console.WriteLine("ユーザーが存在します");
}

Anyは、1件でも見つかればtrueを返せます。存在チェックにはAnyを使う、と覚えておくとよいです。

6-6. ToList()を呼ばないと結果が変わる理由がわからない

LINQの多くのメソッドは、すぐには処理を実行しません。

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

この時点では、まだ抽出処理は確定していない場合があります。

ToListを呼ぶと、その時点で結果がListとして確定します。

C#
var list = numbers.Where(n => n >= 3).ToList();

この違いは、LINQの遅延実行と即時実行に関係しています。

6-7. 遅延実行を理解できず予期しない動作になる

次のコードを見てください。

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

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

numbers.Add(4);

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

この結果には、2、3、4が含まれます。

queryを作った時点ではなく、foreachで実行された時点のnumbersが使われるためです。

これが遅延実行です。結果を固定したい場合は、ToListを使います。

C#
var list = numbers.Where(n => n >= 2).ToList();

6-8. null参照で例外が発生する

LINQ内でプロパティにアクセスするとき、nullに注意が必要です。

C#
var result = users.Where(user => user.Name.Length >= 5);

Nameがnullの場合、例外が発生します。

安全に書くなら、nullチェックを入れます。

C#
var result = users.Where(user => user.Name != null && user.Name.Length >= 5);

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

C#
var result = users.Where(user => user.Name?.Length >= 5);

6-9. LINQを使いすぎて逆に読みにくくなる

LINQは便利ですが、何でもLINQで書けばよいわけではありません。

次のようにメソッドチェーンが長くなりすぎると、初心者だけでなく経験者にも読みにくくなります。

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

改行すると読みやすくなります。

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();

それでも読みにくい場合は、途中結果を変数に分けたり、foreachに戻したりする判断も大切です。

7. LINQの遅延実行と即時実行をわかりやすく解説

LINQを理解するうえで重要なのが、遅延実行と即時実行です。

ここを理解していないと、「なぜこのタイミングで結果が変わるのか」「なぜDBに何度もアクセスされるのか」といった問題につながります。

7-1. 遅延実行とは何か

遅延実行とは、LINQを書いた時点では処理が実行されず、実際に結果が必要になったタイミングで処理される仕組みです。

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

この時点では、まだnumbersをすべて調べているとは限りません。

foreachで回したときに、初めて条件判定が行われます。

C#
foreach (var number in query)
{
Console.WriteLine(number);
}

WhereやSelectなどは、基本的に遅延実行されます。

7-2. foreachで初めて処理が実行される仕組み

次のコードを見ると、遅延実行の動きがわかります。

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

var query = numbers.Where(n =>
{
Console.WriteLine($"判定: {n}");
return n >= 3;
});

Console.WriteLine("foreach開始");

foreach (var number in query)
{
Console.WriteLine($"結果: {number}");
}

Whereを書いた時点では、まだ「判定」は表示されません。

foreachが始まってから、1つずつ判定されます。

このように、LINQは必要になったタイミングで処理されることがあります。

7-3. ToList・ToArray・Countで即時実行される理由

ToListやToArrayは、結果を具体的なコレクションに変換するため、その場で処理を実行します。

C#
var list = numbers.Where(n => n >= 3).ToList();

Countも件数を確定する必要があるため、即時実行されます。

C#
int count = numbers.Where(n => n >= 3).Count();

Sum、Average、Max、Min、First、Anyなども、結果を返すためにその時点で処理が実行されます。

7-4. 遅延実行で起きやすいバグの例

遅延実行で起きやすいバグの1つが、元のコレクションを後から変更してしまうケースです。

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

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

numbers.Clear();

var result = query.ToList();

この場合、resultは空になります。queryを作ったあとにnumbersをClearしているためです。

queryを作った時点の結果を保持したいなら、すぐToListを呼びます。

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

numbers.Clear();

この場合、resultには2と3が残ります。

7-5. 遅延実行を安全に使うためのポイント

遅延実行を安全に使うには、結果を固定したいタイミングでToListやToArrayを使うことが大切です。

ただし、何でもToListすればよいわけではありません。不要なToListはメモリ使用量やパフォーマンスに影響します。

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

データをさらにLINQで加工するだけなら、IEnumerableのままで問題ありません。

C#
var query = users
.Where(user => user.IsActive)
.OrderBy(user => user.Name);

画面表示、戻り値、キャッシュ、複数回の再利用などで結果を確定したい場合はToListします。

C#
var activeUsers = users
.Where(user => user.IsActive)
.ToList();

8. LINQを書くときの実践的なコツ

LINQは短く書ける反面、書き方によっては読みにくくなります。

実務では、「短さ」よりも「読みやすさ」と「意図の伝わりやすさ」が重要です。

8-1. 1行に詰め込みすぎない

LINQは1行で書けますが、長くなる場合は避けましょう。

読みにくい例です。

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

読みやすい例です。

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

メソッドごとに改行すると、処理の流れが追いやすくなります。

8-2. メソッドチェーンを読みやすく改行する

メソッドチェーンは、処理の順番が上から下に流れるように書くと読みやすくなります。

C#
var result = orders
.Where(order => order.IsPaid)
.Where(order => order.Amount >= 1000)
.OrderByDescending(order => order.OrderDate)
.Select(order => new
{
order.Id,
order.Amount,
order.OrderDate
})
.ToList();

絞り込み、並べ替え、変換、確定という流れがわかりやすくなります。

8-3. 変数名をわかりやすくする

ラムダ式の変数名は自由に決められます。

短いコードではxやpでも問題ありませんが、初心者やチーム開発では意味のある名前を使うほうが読みやすいです。

C#
var result = products.Where(product => product.Price >= 1000);
C#
var result = users.Where(user => user.IsActive);

変数名を見るだけで、何を扱っているのかがわかります。

8-4. 複雑な条件はメソッドに切り出す

Whereの条件が長くなる場合は、メソッドに切り出すと読みやすくなります。

C#
var result = users
.Where(user => IsTargetUser(user))
.ToList();
C#
bool IsTargetUser(User user)
{
return user.IsActive
&& user.Age >= 20
&& user.Name != null;
}

条件に名前をつけることで、何を判定しているのかが明確になります。

8-5. foreachのほうが読みやすいケースを見極める

LINQは便利ですが、複雑な分岐や複数の副作用がある処理には向かないことがあります。

たとえば、ログ出力、状態変更、複数の条件分岐を同時に行う場合は、foreachのほうが読みやすいことがあります。

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

Console.WriteLine(user.Name);
}

LINQは「データを取り出す・変換する・集計する」処理に向いています。状態を変更する処理を無理にLINQで書く必要はありません。

8-6. パフォーマンスを意識して不要なToListを避ける

ToListは便利ですが、呼ぶたびに新しいListを作ります。

不要なToListを何度も使うと、メモリ使用量が増えたり、処理が重くなったりすることがあります。

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

このような書き方は、途中でListにする必要がなければ避けられます。

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

ToListは、結果を確定したい最後のタイミングで使うのが基本です。

8-7. LINQとデバッグのコツ

LINQのメソッドチェーンが長いと、途中結果がわかりにくいことがあります。

その場合は、処理を分けて変数に入れるとデバッグしやすくなります。

C#
var activeUsers = users
.Where(user => user.IsActive);

var adultUsers = activeUsers
.Where(user => user.Age >= 20);

var result = adultUsers
.Select(user => user.Name)
.ToList();

一時変数を使うことで、それぞれの段階でどのようなデータになっているか確認しやすくなります。

9. LINQとSQL・Entity Frameworkの関係

C# LINQは、データベース操作でもよく使われます。

特にEntity Frameworkを使う場合、LINQで書いた処理がSQLに変換され、データベースに対して実行されます。

9-1. LINQはSQLと何が似ているのか

SQLでは、次のようにデータを絞り込みます。

SQL
SELECT Name
FROM Users
WHERE Age >= 20
ORDER BY Name;

LINQでは、同じような処理をC#で書けます。

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

Where、OrderBy、Selectといった考え方はSQLに似ています。

ただし、LINQはC#のコードとして書けるため、型チェックや補完が効きやすいというメリットがあります。

9-2. LINQ to ObjectsとLINQ to Entitiesの違い

LINQ to Objectsは、Listや配列など、メモリ上のオブジェクトに対してLINQを使うことです。

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

このusersがList<User>なら、LINQ to Objectsです。

LINQ to Entitiesは、Entity Frameworkを通してデータベースに対してLINQを使うことです。

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

この場合、LINQの式がSQLに変換され、データベースで実行されます。

同じWhereでも、対象がListなのかDBなのかで実行場所が変わる点に注意が必要です。

9-3. Entity FrameworkでLINQが使われる理由

Entity FrameworkでLINQが使われる理由は、C#のコードでデータベース検索を書けるからです。

SQL文字列を直接組み立てるよりも、型安全で、プロパティ名の補完も使いやすくなります。

C#
var activeUsers = db.Users
.Where(user => user.IsActive)
.OrderBy(user => user.Name)
.ToList();

このように書くと、Entity Frameworkが適切なSQLに変換してデータベースへ問い合わせます。

C#のコードとしてデータ取得処理を書けることが、LINQとEntity Frameworkの大きなメリットです。

9-4. IQueryable使用時に注意すべきこと

IQueryableは、LINQの処理をすぐに実行せず、式として保持します。

Entity Frameworkでは、ToListやFirstOrDefaultなどを呼ぶタイミングでSQLが実行されます。

C#
var query = db.Users.Where(user => user.IsActive);

この時点では、まだデータベースに問い合わせていない場合があります。

C#
var users = query.ToList();

ここでSQLが実行され、結果が取得されます。

IQueryableでは、C#のすべてのメソッドがSQLに変換できるわけではありません。独自メソッドをWhereの中で使うと、変換できずにエラーになる場合があります。

9-5. DBアクセスでLINQを書くときの注意点

データベースに対してLINQを書くときは、必要なデータだけを取得することが重要です。

悪い例です。

C#
var users = db.Users.ToList();

var result = users.Where(user => user.Age >= 20);

この書き方では、すべてのユーザーをDBから取得したあと、C#側で絞り込んでいます。

良い例です。

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

この書き方なら、条件がSQLに変換され、DB側で絞り込まれます。

Entity FrameworkでLINQを書くときは、ToListを呼ぶタイミングに注意しましょう。

10. C# LINQの学習ロードマップ

C# LINQは範囲が広いため、順番に学ぶことが大切です。

最初からGroupByやIQueryableまで完璧に理解しようとすると難しく感じます。まずは基本メソッドから始めましょう。

10-1. まず覚えるべき基本メソッド

最初に覚えるべきLINQメソッドは、次のあたりです。

Whereは絞り込み、Selectは変換、OrderByは並べ替え、FirstOrDefaultは1件取得、Anyは存在チェック、ToListは結果の確定です。

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

この基本パターンを理解できれば、多くのLINQコードが読めるようになります。

10-2. 次に理解したいラムダ式と遅延実行

基本メソッドに慣れたら、ラムダ式を理解しましょう。

C#
user => user.Age >= 20

これは、userを受け取って、Ageが20以上かどうかを返す処理です。

次に、遅延実行を理解します。

C#
var query = users.Where(user => user.IsActive);

この時点では、まだ処理が確定していない場合があります。

C#
var list = query.ToList();

ToListで結果が確定します。

ラムダ式と遅延実行を理解すると、LINQで起きる多くの疑問が解消されます。

10-3. 実務でよく使うLINQパターン

実務では、次のようなパターンがよく使われます。

条件で絞り込んで一覧表示するパターンです。

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

並べ替えて上位件数を取得するパターンです。

C#
var topProducts = products
.OrderByDescending(product => product.Price)
.Take(5)
.ToList();

グループ化して集計するパターンです。

C#
var totals = sales
.GroupBy(sale => sale.Category)
.Select(group => new
{
Category = group.Key,
Total = group.Sum(sale => sale.Amount)
})
.ToList();

IDをキーにしてDictionary化するパターンです。

C#
var userMap = users.ToDictionary(user => user.Id);

これらを使えるようになると、実務のデータ処理でかなり役立ちます。

10-4. 初心者向け練習問題

C# LINQを身につけるには、手を動かして練習するのが一番です。

たとえば、次のような問題を解いてみましょう。

1から10までの数値から偶数だけを取り出す。

C#
var numbers = Enumerable.Range(1, 10).ToList();

var result = numbers
.Where(number => number % 2 == 0)
.ToList();

文字列の一覧から、5文字以上の名前だけを取り出す。

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

var result = names
.Where(name => name.Length >= 5)
.ToList();

商品一覧を価格が高い順に並べる。

C#
var result = products
.OrderByDescending(product => product.Price)
.ToList();

ユーザー一覧から名前だけを取り出す。

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

売上データをカテゴリ別に合計する。

C#
var result = sales
.GroupBy(sale => sale.Category)
.Select(group => new
{
Category = group.Key,
Total = group.Sum(sale => sale.Amount)
})
.ToList();

このような基本問題を繰り返すと、LINQの感覚が自然に身につきます。

10-5. LINQを使いこなすためのおすすめ学習順序

LINQを学ぶ順序は、次の流れがおすすめです。

まず、Listや配列などのコレクションに慣れます。

次に、Where、Select、OrderBy、ToListを使って基本的なデータ処理を書きます。

そのあと、FirstOrDefault、Any、Count、Sum、Averageなどの取得・判定・集計メソッドを覚えます。

さらに、GroupBy、Distinct、Take、Skip、ToDictionaryなどを学ぶと、実務で使える幅が広がります。

最後に、遅延実行、IEnumerable、IQueryable、Entity Frameworkとの関係を理解すると、LINQをより安全に使えるようになります。

最初から難しい概念をすべて理解する必要はありません。まずは「Listから条件に合うデータを取り出す」ことから始めるのが近道です。

まとめ

C# LINQは、Listや配列、Dictionary、データベースなどのデータに対して、検索、抽出、並べ替え、集計、変換を行うための便利な機能です。

初心者がまず覚えるべき基本は、Where、Select、OrderBy、FirstOrDefault、Any、ToListです。

Whereは条件で絞り込み、Selectは必要な形に変換し、OrderByは並べ替えます。FirstOrDefaultは1件取得、Anyは存在チェック、ToListは結果の確定に使います。

LINQを学ぶうえでは、ラムダ式と遅延実行の理解も重要です。特に、WhereやSelectはすぐに実行されるとは限らず、foreachやToListなどのタイミングで処理されることがあります。

また、LINQは便利ですが、使いすぎると読みにくくなる場合もあります。1行に詰め込みすぎず、メソッドチェーンを改行し、変数名をわかりやすくすることが大切です。

Entity Frameworkを使う場合は、LINQがSQLに変換されるため、ToListを呼ぶタイミングやIQueryableの扱いにも注意が必要です。

C# LINQを使いこなせるようになると、データ処理のコードを短く、読みやすく、保守しやすく書けるようになります。まずは基本メソッドを使った簡単な例から練習し、少しずつ実践的なパターンに慣れていきましょう。