C#でプログラムを書いていて、こんな経験ありませんか?
「100個のデータから、条件に合うものだけを取り出したい」
「リストの中身を全部2倍にしたい」
「一番大きい値を見つけたい」
そのたびに、for文やforeach文を書いて、if文で条件をチェックして、新しいリストを作って…と、何行ものコードを書く必要がありました。
実は、C#にはLINQ(リンク)という素晴らしい機能があります。
LINQを使えば、複雑なデータ処理も驚くほどシンプルに書けるんです。
この記事では、プログラミング初心者の方でも理解できるように、LINQの基本から実用的な使い方まで、段階的に解説します。
LINQって何?なぜこんなに便利なの?

LINQの正体
LINQは「Language Integrated Query(言語統合クエリ)」の略です。
難しそうに聞こえますが、簡単に言うと「C#の中に組み込まれた、データを簡単に操作するための仕組み」です。
従来の方法(LINQ使用前)
// 偶数だけを取り出したい場合
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> evenNumbers = new List<int>();
foreach (int number in numbers)
{
if (number % 2 == 0)
{
evenNumbers.Add(number);
}
}
// 結果: 2, 4, 6, 8, 10
LINQを使った方法
// 偶数だけを取り出したい場合
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
// 結果: 2, 4, 6, 8, 10
7行が1行に! これがLINQの威力です。
LINQが使えるデータの種類
LINQは、様々な種類のデータに使えます。
基本的なコレクション
- 配列:
int[] numbers = {1, 2, 3, 4, 5};
- List:
List<string> names = new List<string>();
- Dictionary:
Dictionary<string, int> scores;
データベース
- Entity Frameworkを使ったデータベース操作
- SQL Server、MySQLなどとの連携
その他
- XMLファイルの操作
- JSONデータの処理
LINQのメリット
1. 圧倒的にコードが短くなる
- 10行のループが1行になることも
- 可読性が大幅に向上
2. 直感的で理解しやすい
- 英語のような自然な表現
- 「どこから」「何を」「どうする」が明確
3. バグが起きにくい
- ループの書き間違いが減る
- インデックスエラーの心配がない
4. 保守しやすい
- 修正が簡単
- 他の人が読みやすい
LINQの基本構文を覚えよう

ラムダ式の基本
LINQではラムダ式という記法を使います。
最初は慣れないかもしれませんが、とても便利です。
ラムダ式の基本形
(引数) => 処理
具体例
// xという引数を受け取って、x * 2を返す
x => x * 2
// numberという引数を受け取って、偶数かどうかを判定
number => number % 2 == 0
// personという引数を受け取って、年齢を返す
person => person.Age
従来の書き方との比較
// 従来の方法(無名メソッド)
numbers.Where(delegate(int n) { return n % 2 == 0; });
// ラムダ式(簡潔!)
numbers.Where(n => n % 2 == 0);
LINQの基本パターン
データ.メソッド(条件) のパターン
// データから条件に合うものを抽出
var result = データ.Where(条件);
// データの各要素を変換
var result = データ.Select(変換処理);
// データを並び替え
var result = データ.OrderBy(並び順);
よく使うLINQメソッド完全ガイド
Where:条件に合うデータを抽出
基本的な使い方
// 数値のリストから10より大きいものを抽出
List<int> numbers = new List<int> { 5, 10, 15, 20, 25 };
var bigNumbers = numbers.Where(n => n > 10);
// 結果: 15, 20, 25
// 文字列リストから特定の文字を含むものを抽出
List<string> names = new List<string> { "Alice", "Bob", "Charlie", "Diana" };
var aNames = names.Where(name => name.Contains("a"));
// 結果: "Alice", "Diana"
複数条件の指定
// 10より大きく、25未満の数値
var middleNumbers = numbers.Where(n => n > 10 && n < 25);
// 文字数が4文字以上で、Aで始まる名前
var longANames = names.Where(name => name.Length >= 4 && name.StartsWith("A"));
Select:データを変換
基本的な使い方
// 数値を2倍にする
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var doubledNumbers = numbers.Select(n => n * 2);
// 結果: 2, 4, 6, 8, 10
// 文字列を大文字に変換
List<string> names = new List<string> { "alice", "bob", "charlie" };
var upperNames = names.Select(name => name.ToUpper());
// 結果: "ALICE", "BOB", "CHARLIE"
複雑な変換
// オブジェクトのプロパティを取得
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 }
};
// 名前だけのリストを作成
var names = people.Select(p => p.Name);
// 新しいオブジェクトを作成
var summaries = people.Select(p => new {
Name = p.Name,
Category = p.Age >= 30 ? "大人" : "若者"
});
OrderBy / OrderByDescending:並び替え
基本的な並び替え
List<int> numbers = new List<int> { 3, 1, 4, 1, 5, 9, 2, 6 };
// 昇順(小さい順)
var ascending = numbers.OrderBy(n => n);
// 結果: 1, 1, 2, 3, 4, 5, 6, 9
// 降順(大きい順)
var descending = numbers.OrderByDescending(n => n);
// 結果: 9, 6, 5, 4, 3, 2, 1, 1
複数条件での並び替え
// 年齢順、同じ年齢なら名前順
var sortedPeople = people
.OrderBy(p => p.Age)
.ThenBy(p => p.Name);
FirstOrDefault / LastOrDefault:最初・最後の要素
基本的な使い方
List<int> numbers = new List<int> { 10, 20, 30, 40, 50 };
// 最初の要素
var first = numbers.FirstOrDefault();
// 結果: 10
// 条件に合う最初の要素
var firstBig = numbers.FirstOrDefault(n => n > 25);
// 結果: 30
// 最後の要素
var last = numbers.LastOrDefault();
// 結果: 50
注意:null チェックが重要
List<int> emptyList = new List<int>();
var result = emptyList.FirstOrDefault();
// result は 0(intのデフォルト値)
List<string> emptyStringList = new List<string>();
var stringResult = emptyStringList.FirstOrDefault();
// stringResult は null
// 安全な使い方
if (stringResult != null)
{
Console.WriteLine(stringResult);
}
Count / Any / All:件数と存在チェック
Count:件数を数える
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 全体の件数
var totalCount = numbers.Count();
// 結果: 10
// 条件に合う件数
var evenCount = numbers.Count(n => n % 2 == 0);
// 結果: 5
Any:条件に合うものが存在するか
// 偶数が存在するか
var hasEven = numbers.Any(n => n % 2 == 0);
// 結果: true
// 100より大きい数が存在するか
var hasBig = numbers.Any(n => n > 100);
// 結果: false
All:すべてが条件に合うか
// すべてが正数か
var allPositive = numbers.All(n => n > 0);
// 結果: true
// すべてが偶数か
var allEven = numbers.All(n => n % 2 == 0);
// 結果: false
メソッドチェーンで複雑な処理も簡単に

メソッドチェーンの基本
LINQの真骨頂は、複数のメソッドを**チェーン(数珠つなぎ)**にできることです。
基本的なチェーン
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 偶数を抽出して、2倍にして、降順に並べる
var result = numbers
.Where(n => n % 2 == 0) // 偶数を抽出: 2, 4, 6, 8, 10
.Select(n => n * 2) // 2倍にする: 4, 8, 12, 16, 20
.OrderByDescending(n => n); // 降順に並べる: 20, 16, 12, 8, 4
実用的なメソッドチェーン例
例1:学生の成績処理
public class Student
{
public string Name { get; set; }
public int Score { get; set; }
public string Subject { get; set; }
}
List<Student> students = new List<Student>
{
new Student { Name = "Alice", Score = 85, Subject = "Math" },
new Student { Name = "Bob", Score = 92, Subject = "Math" },
new Student { Name = "Charlie", Score = 78, Subject = "English" },
new Student { Name = "Diana", Score = 95, Subject = "Math" }
};
// 数学の成績が80点以上の学生を、得点順(降順)で取得
var topMathStudents = students
.Where(s => s.Subject == "Math") // 数学の学生のみ
.Where(s => s.Score >= 80) // 80点以上
.OrderByDescending(s => s.Score) // 得点順(高い順)
.Select(s => s.Name) // 名前だけ取得
.ToList(); // リストに変換
// 結果: ["Diana", "Bob", "Alice"]
例2:売上データの集計
public class Sale
{
public string Product { get; set; }
public decimal Amount { get; set; }
public DateTime Date { get; set; }
}
List<Sale> sales = new List<Sale>
{
new Sale { Product = "PC", Amount = 1000, Date = new DateTime(2024, 1, 15) },
new Sale { Product = "Phone", Amount = 800, Date = new DateTime(2024, 1, 20) },
new Sale { Product = "PC", Amount = 1200, Date = new DateTime(2024, 2, 10) }
};
// 今年のPC売上の平均金額
var avgPcSales = sales
.Where(s => s.Date.Year == 2024) // 今年の売上
.Where(s => s.Product == "PC") // PC の売上
.Select(s => s.Amount) // 金額のみ
.Average(); // 平均値
// 結果: 1100
実践的なLINQ活用例
データベース風の操作
Join操作(結合)
// 部署情報
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
// 従業員情報
public class Employee
{
public string Name { get; set; }
public int DepartmentId { get; set; }
public decimal Salary { get; set; }
}
List<Department> departments = new List<Department>
{
new Department { Id = 1, Name = "開発部" },
new Department { Id = 2, Name = "営業部" }
};
List<Employee> employees = new List<Employee>
{
new Employee { Name = "田中", DepartmentId = 1, Salary = 500000 },
new Employee { Name = "佐藤", DepartmentId = 2, Salary = 450000 },
new Employee { Name = "鈴木", DepartmentId = 1, Salary = 550000 }
};
// 従業員と部署を結合して、部署名付きの情報を取得
var employeeDetails = employees
.Join(departments, // 結合するコレクション
emp => emp.DepartmentId, // 従業員側のキー
dept => dept.Id, // 部署側のキー
(emp, dept) => new // 結合結果
{
EmployeeName = emp.Name,
DepartmentName = dept.Name,
Salary = emp.Salary
})
.ToList();
GroupBy操作(グループ化)
// 部署別の平均給与を計算
var avgSalaryByDept = employees
.GroupBy(emp => emp.DepartmentId) // 部署IDでグループ化
.Select(group => new
{
DepartmentId = group.Key, // グループのキー
AverageSalary = group.Average(emp => emp.Salary), // 平均給与
EmployeeCount = group.Count() // 人数
})
.ToList();
ファイル・文字列処理
CSVファイルの処理
// CSVデータの読み込みと処理
string csvData = @"
Name,Age,City
Alice,25,Tokyo
Bob,30,Osaka
Charlie,35,Tokyo
Diana,28,Kyoto";
var people = csvData
.Split('\n') // 行に分割
.Skip(1) // ヘッダー行をスキップ
.Where(line => !string.IsNullOrEmpty(line)) // 空行を除外
.Select(line => line.Split(',')) // カンマで分割
.Select(parts => new
{
Name = parts[0],
Age = int.Parse(parts[1]),
City = parts[2]
})
.Where(person => person.Age >= 30) // 30歳以上のみ
.OrderBy(person => person.Name) // 名前順
.ToList();
ログファイルの分析
List<string> logLines = new List<string>
{
"2024-01-15 10:30:15 ERROR Database connection failed",
"2024-01-15 10:30:20 INFO User login successful",
"2024-01-15 10:30:25 ERROR File not found",
"2024-01-15 10:30:30 INFO Data processing completed"
};
// エラーログだけを抽出して時刻順に並べる
var errorLogs = logLines
.Where(log => log.Contains("ERROR")) // ERRORを含む行のみ
.Select(log => new
{
Timestamp = log.Substring(0, 19), // 日時部分
Message = log.Substring(26) // メッセージ部分
})
.OrderBy(log => log.Timestamp) // 時刻順
.ToList();
LINQを使うときの注意点と落とし穴

遅延実行(Lazy Evaluation)の理解
LINQの重要な特徴の一つが遅延実行です。
遅延実行とは
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// この時点では実際の処理は実行されない!
var query = numbers.Where(n => n > 3);
// ToList()やforeach等で初めて実行される
var result = query.ToList(); // ここで実際に処理が実行
遅延実行のメリット
- メモリ効率が良い
- 必要になるまで処理を遅らせられる
- クエリの組み立てが柔軟
注意が必要な場面
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var query = numbers.Where(n => n > 3);
// 元のリストを変更
numbers.Add(6);
numbers.Add(7);
// この時点で実行されるため、追加された6,7も処理対象になる
var result = query.ToList(); // 結果: 4, 5, 6, 7
パフォーマンスの考慮事項
メモリ使用量に注意
// 悪い例:不必要にメモリを消費
var badExample = hugelist
.Where(x => x.IsActive)
.ToList() // 全件メモリに読み込み
.Where(x => x.Score > 80)
.ToList(); // また全件メモリに読み込み
// 良い例:遅延実行を活用
var goodExample = hugelist
.Where(x => x.IsActive)
.Where(x => x.Score > 80) // 条件を組み合わせる
.ToList(); // 最後にまとめて実行
複数回の列挙を避ける
// 悪い例:同じクエリを複数回実行
var expensiveQuery = database.Users.Where(u => ComplexOperation(u));
var count = expensiveQuery.Count(); // 1回目の実行
var first = expensiveQuery.FirstOrDefault(); // 2回目の実行
// 良い例:一度だけ実行して結果を保存
var results = expensiveQuery.ToList(); // 1回だけ実行
var count = results.Count();
var first = results.FirstOrDefault();
null チェックの重要性
FirstOrDefault の罠
List<string> names = new List<string>();
// 空のリストの場合、nullが返される
var firstName = names.FirstOrDefault();
if (firstName != null) // 必須のチェック
{
Console.WriteLine(firstName.Length);
}
// より安全な書き方
var firstName2 = names.FirstOrDefault();
Console.WriteLine(firstName2?.Length ?? 0); // null条件演算子を使用
Where の後の FirstOrDefault
List<Person> people = GetPeopleList();
// 条件に合う人が見つからない場合、nullが返される
var targetPerson = people
.Where(p => p.Age > 100) // 100歳超の人はいない?
.FirstOrDefault();
if (targetPerson != null)
{
Console.WriteLine(targetPerson.Name);
}
読みやすさとのバランス
複雑すぎるクエリは避ける
// 悪い例:読みにくい
var result = data
.Where(x => x.Status == "Active" && x.Category != null)
.GroupBy(x => x.Category)
.Where(g => g.Count() > 5)
.SelectMany(g => g.Where(x => x.Score > g.Average(y => y.Score)))
.OrderByDescending(x => x.Score)
.Take(10)
.Select(x => new { x.Name, x.Score, x.Category })
.ToList();
// 良い例:段階的に分割
var activeItems = data.Where(x => x.Status == "Active" && x.Category != null);
var largeCategories = activeItems
.GroupBy(x => x.Category)
.Where(g => g.Count() > 5);
var result = largeCategories
.SelectMany(g => g.Where(x => x.Score > g.Average(y => y.Score)))
.OrderByDescending(x => x.Score)
.Take(10)
.Select(x => new { x.Name, x.Score, x.Category })
.ToList();
LINQ クエリ構文も知っておこう

メソッド構文 vs クエリ構文
これまで紹介してきたのはメソッド構文でしたが、LINQにはクエリ構文という書き方もあります。
メソッド構文(今まで使ってきた方法)
var result = students
.Where(s => s.Score >= 80)
.OrderByDescending(s => s.Score)
.Select(s => s.Name);
クエリ構文(SQL風の書き方)
var result = from s in students
where s.Score >= 80
orderby s.Score descending
select s.Name;
クエリ構文の利点
複雑な結合処理
// メソッド構文(ちょっと読みにくい)
var result1 = employees
.Join(departments,
emp => emp.DepartmentId,
dept => dept.Id,
(emp, dept) => new { emp, dept })
.Where(x => x.emp.Salary > 500000)
.Select(x => new { x.emp.Name, x.dept.Name });
// クエリ構文(SQL に慣れていれば読みやすい)
var result2 = from emp in employees
join dept in departments on emp.DepartmentId equals dept.Id
where emp.Salary > 500000
select new { emp.Name, dept.Name };
複数の from(複数のコレクションを扱う)
List<string> categories = new List<string> { "食品", "電化製品" };
List<string> products = new List<string> { "りんご", "テレビ", "みかん", "冷蔵庫" };
// すべての組み合わせを作成
var combinations = from category in categories
from product in products
select new { Category = category, Product = product };
まとめ
LINQの基本から実践的な使い方まで、いかがでしたか?
今日覚えた重要なポイント
- LINQ は複雑なデータ処理を1行で書ける魔法の機能
- Where, Select, OrderBy が基本の3大メソッド
- メソッドチェーンで複雑な処理も簡潔に
- 遅延実行とパフォーマンスに注意
コメント