C# LINQのソート完全ガイド|OrderBy・ThenByで昇順/降順/複数条件の並べ替えをわかりやすく解説
はじめに
C#でコレクションのデータを並べ替えたいとき、よく使われるのがLINQのOrderBy、OrderByDescending、ThenBy、ThenByDescendingです。
たとえば、数値を小さい順に並べる、名前を五十音順に並べる、売上の高い順に並べる、日付の新しい順に並べる、部署順かつ年齢順に並べる、といった処理はLINQを使うとシンプルに書けます。
C#var sorted = users.OrderBy(user => user.Age);
LINQのソートは読みやすく、List<T>、配列、Dictionary、オブジェクトの一覧など、さまざまなデータに使えるのが特徴です。
この記事では、C# LINQのソートについて、昇順、降順、複数条件、null対応、カスタムソート、よくあるミスまで、実践で使える形でわかりやすく解説します。
1. C# LINQのソートとは?まず押さえる基本
LINQのソートとは、コレクション内の要素を指定した条件に従って並べ替える処理です。
C#では、System.Linq名前空間を使うことで、配列やリストなどに対して簡単に並べ替えを行えます。
C#using System.Linq;
LINQのソートでは、「何を基準に並べ替えるか」をラムダ式で指定するのが一般的です。
C#var result = items.OrderBy(x => x.Price);
この例では、itemsをPriceの昇順でソートしています。
1-1. LINQで並べ替えを行う代表的なメソッド
LINQでソートするときによく使うメソッドは、主に次の4つです。
| メソッド | 用途 |
|---|---|
OrderBy | 昇順で並べ替える |
OrderByDescending | 降順で並べ替える |
ThenBy | 2つ目以降の条件を昇順で追加する |
ThenByDescending | 2つ目以降の条件を降順で追加する |
基本的には、最初の並べ替え条件にOrderByまたはOrderByDescendingを使い、追加条件にThenByまたはThenByDescendingを使います。
C#var result = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Age);
このコードは、まず部署順に並べ、同じ部署の中では年齢の昇順に並べます。
1-2. OrderByとList<T>.Sortの違い
C#でソートする方法には、LINQのOrderBy以外にList<T>.Sortもあります。
大きな違いは、元のリストを変更するかどうかです。
C#var numbers = new List<int> { 3, 1, 2 };
var sorted = numbers.OrderBy(x => x).ToList();
この場合、sortedは1, 2, 3になりますが、元のnumbersは3, 1, 2のままです。
一方、List<T>.Sortは元のリスト自体を並べ替えます。
C#var numbers = new List<int> { 3, 1, 2 };
numbers.Sort();
この場合、numbers自体が1, 2, 3に変更されます。
元データを残したい場合はLINQのOrderBy、元のリストを直接変更してよい場合はList<T>.Sortが向いています。
1-3. LINQのソートは元のコレクションを変更しない
LINQの重要な特徴として、OrderByは元のコレクションを変更しません。
C#var scores = new List<int> { 80, 100, 60 };
var sortedScores = scores.OrderBy(score => score);
Console.WriteLine(string.Join(", ", scores));
// 80, 100, 60
Console.WriteLine(string.Join(", ", sortedScores));
// 60, 80, 100
元のList<T>はそのままで、並べ替えられた結果が別のシーケンスとして返されます。
そのため、ソート後の結果を使いたい場合は、戻り値を変数に代入する必要があります。
C#scores.OrderBy(score => score); // 結果を使っていないため意味がない
var sortedScores = scores.OrderBy(score => score); // 正しい
1-4. IEnumerableとIOrderedEnumerableの関係
LINQのOrderByは、IOrderedEnumerable<T>を返します。
C#IOrderedEnumerable<int> sorted = numbers.OrderBy(x => x);
IOrderedEnumerable<T>は、ソート済みの順序情報を持つIEnumerable<T>です。
ThenByやThenByDescendingは、このIOrderedEnumerable<T>に対して追加の並べ替え条件を指定するために使います。
C#var result = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Name);
最初のOrderByで並び順の土台を作り、その後のThenByで同順位の要素に対する追加ルールを指定する、というイメージです。
2. OrderByで昇順にソートする方法
OrderByは、指定したキーを基準に昇順で並べ替えるメソッドです。
昇順とは、小さいものから大きいもの、古いものから新しいもの、文字列であれば通常の比較順に並べることです。
2-1. 数値を昇順に並べ替える基本例
数値を小さい順に並べるには、次のように書きます。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
var sortedNumbers = numbers.OrderBy(x => x);
foreach (var number in sortedNumbers)
{
Console.WriteLine(number);
}
実行結果は次のようになります。
C#1
2
3
5
8
x => xは、「要素そのものをソートキーにする」という意味です。
2-2. 文字列を昇順に並べ替える基本例
文字列のリストもOrderByでソートできます。
C#var names = new List<string> { "Taro", "Hanako", "Jiro", "Akira" };
var sortedNames = names.OrderBy(name => name);
foreach (var name in sortedNames)
{
Console.WriteLine(name);
}
結果は次のようになります。
C#Akira
Hanako
Jiro
Taro
文字列の場合も、基本的には比較結果に従って昇順に並びます。
日本語文字列や大文字・小文字の扱いでは、カルチャや比較方法によって結果が変わることがあるため、必要に応じて比較方法を明示すると安全です。
2-3. オブジェクトのプロパティを指定して昇順ソートする
実務では、数値や文字列そのものよりも、オブジェクトの一覧を特定のプロパティで並べ替えるケースが多いです。
C#public class Product
{
public string Name { get; set; } = "";
public int Price { get; set; }
}
価格の安い順に並べる場合は、次のように書きます。
C#var products = new List<Product>
{
new Product { Name = "Mouse", Price = 2000 },
new Product { Name = "Keyboard", Price = 5000 },
new Product { Name = "Monitor", Price = 30000 }
};
var sortedProducts = products.OrderBy(product => product.Price);
foreach (var product in sortedProducts)
{
Console.WriteLine($"{product.Name}: {product.Price}");
}
結果は次のようになります。
C#Mouse: 2000
Keyboard: 5000
Monitor: 30000
このように、OrderBy(product => product.Price)と書くことで、Priceプロパティを基準に昇順ソートできます。
2-4. ToList・ToArrayで結果をリストや配列に変換する
OrderByの戻り値は、List<T>そのものではありません。
そのため、ソート結果をリストとして扱いたい場合はToList()を使います。
C#var sortedList = numbers.OrderBy(x => x).ToList();
配列にしたい場合はToArray()を使います。
C#var sortedArray = numbers.OrderBy(x => x).ToArray();
実務では、後続処理でリストとして扱いたい場合や、結果を固定したい場合にToList()を付けることが多いです。
C#var sortedProducts = products
.OrderBy(product => product.Price)
.ToList();
3. OrderByDescendingで降順にソートする方法
OrderByDescendingは、指定したキーを基準に降順で並べ替えるメソッドです。
降順とは、大きいものから小さいもの、新しいものから古いもの、評価が高いものから低いものへ並べることです。
3-1. 数値を降順に並べ替える基本例
数値を大きい順に並べるには、OrderByDescendingを使います。
C#var scores = new List<int> { 80, 100, 60, 90 };
var sortedScores = scores.OrderByDescending(score => score);
foreach (var score in sortedScores)
{
Console.WriteLine(score);
}
実行結果は次のようになります。
C#100
90
80
60
ランキングや売上順、スコア順などでは降順ソートがよく使われます。
3-2. 文字列を降順に並べ替える基本例
文字列も降順に並べ替えられます。
C#var names = new List<string> { "Taro", "Hanako", "Jiro", "Akira" };
var sortedNames = names.OrderByDescending(name => name);
foreach (var name in sortedNames)
{
Console.WriteLine(name);
}
結果は次のようになります。
C#Taro
Jiro
Hanako
Akira
通常の昇順とは逆の順番で並びます。
3-3. オブジェクトのプロパティを指定して降順ソートする
オブジェクトを特定のプロパティで降順ソートする場合も、書き方はシンプルです。
C#var products = new List<Product>
{
new Product { Name = "Mouse", Price = 2000 },
new Product { Name = "Keyboard", Price = 5000 },
new Product { Name = "Monitor", Price = 30000 }
};
var sortedProducts = products.OrderByDescending(product => product.Price);
foreach (var product in sortedProducts)
{
Console.WriteLine($"{product.Name}: {product.Price}");
}
結果は次のようになります。
C#Monitor: 30000
Keyboard: 5000
Mouse: 2000
高い順、最新順、優先度順などはOrderByDescendingで表現できます。
3-4. 昇順と降順を使い分ける判断基準
昇順と降順は、一覧を見たユーザーが何を優先して確認したいかで決めるとわかりやすくなります。
価格の安い順、日付の古い順、名前順、番号順などは昇順が向いています。
C#var result = products.OrderBy(product => product.Price);
一方、売上の高い順、作成日の新しい順、スコアの高い順、優先度の高い順などは降順が向いています。
C#var result = articles.OrderByDescending(article => article.CreatedAt);
一覧画面では、「初期表示でユーザーが最も見たいデータを上に出す」ことを意識すると、自然に昇順・降順を選べます。
4. ThenBy・ThenByDescendingで複数条件ソートする方法
複数条件でソートしたい場合は、OrderByまたはOrderByDescendingの後に、ThenByまたはThenByDescendingをつなげます。
たとえば、「部署順に並べ、同じ部署の中では年齢順に並べる」といった処理です。
4-1. 複数条件ソートが必要になるケース
複数条件ソートは、最初の条件だけでは並び順を決めきれない場合に使います。
たとえば、次のようなケースです。
C#public class Employee
{
public string Department { get; set; } = "";
public string Name { get; set; } = "";
public int Age { get; set; }
public DateTime JoinedAt { get; set; }
}
社員一覧で、部署順に並べたうえで、同じ部署内では名前順に並べたい場合があります。
C#var result = employees
.OrderBy(employee => employee.Department)
.ThenBy(employee => employee.Name);
売上一覧なら、売上額の高い順に並べ、同じ売上額なら日付の古い順に並べる、といった使い方もあります。
4-2. OrderByの後にThenByをつなげる基本形
複数条件ソートの基本形は次のとおりです。
C#var result = collection
.OrderBy(x => x.FirstKey)
.ThenBy(x => x.SecondKey);
具体例を見てみましょう。
C#var employees = new List<Employee>
{
new Employee { Department = "Sales", Name = "Taro", Age = 30 },
new Employee { Department = "Sales", Name = "Akira", Age = 25 },
new Employee { Department = "Dev", Name = "Hanako", Age = 28 },
new Employee { Department = "Dev", Name = "Jiro", Age = 24 }
};
var sortedEmployees = employees
.OrderBy(employee => employee.Department)
.ThenBy(employee => employee.Name);
この場合、まずDepartmentで並び替え、同じDepartmentの中でNameを昇順に並べます。
4-3. 昇順と降順を組み合わせて並べ替える
複数条件では、昇順と降順を自由に組み合わせられます。
C#var result = employees
.OrderBy(employee => employee.Department)
.ThenByDescending(employee => employee.Age);
この例では、部署は昇順、同じ部署内では年齢が高い順になります。
反対に、最初の条件を降順にすることもできます。
C#var result = employees
.OrderByDescending(employee => employee.JoinedAt)
.ThenBy(employee => employee.Name);
この場合、入社日の新しい順に並べ、同じ入社日なら名前順に並べます。
4-4. ThenByを使わずOrderByを連続で書くとどうなるか
複数条件ソートでよくあるミスが、OrderByを連続で書いてしまうことです。
C#var result = employees
.OrderBy(employee => employee.Department)
.OrderBy(employee => employee.Name);
この書き方では、2回目のOrderByが新しい主要な並べ替え条件になります。
つまり、「部署順に並べたあと、同じ部署内で名前順にする」という意図なら、ThenByを使うべきです。
C#var result = employees
.OrderBy(employee => employee.Department)
.ThenBy(employee => employee.Name);
複数条件ソートでは、最初だけOrderBy、2つ目以降はThenByと覚えると間違いにくくなります。
4-5. 複数プロパティを条件にした実践例
実務では、3つ以上の条件で並べ替えることもあります。
たとえば、社員一覧を次の条件で並べたいとします。
部署名の昇順
役職の優先度順
入社日の古い順
名前の昇順
この場合は、次のように書けます。
C#var sortedEmployees = employees
.OrderBy(employee => employee.Department)
.ThenBy(employee => employee.PositionRank)
.ThenBy(employee => employee.JoinedAt)
.ThenBy(employee => employee.Name);
ThenByは複数回つなげられるため、条件が増えても読みやすく表現できます。
5. LINQソートの実践パターン
ここからは、実際の開発でよく使うLINQソートのパターンを紹介します。
List<T>、配列、Dictionary、DateTime、nullを含むデータ、文字列比較など、現場で使う場面を想定して見ていきましょう。
5-1. List<T>をLINQでソートする
List<T>をLINQでソートする場合は、OrderByやOrderByDescendingをそのまま使えます。
C#var numbers = new List<int> { 10, 3, 7, 1 };
var sortedNumbers = numbers.OrderBy(x => x).ToList();
オブジェクトのリストでも同じです。
C#var sortedProducts = products
.OrderBy(product => product.Price)
.ToList();
元のリストを変更せず、ソート済みの新しいリストが欲しい場合に便利です。
5-2. 配列をLINQでソートする
配列に対してもLINQのソートは使えます。
C#int[] numbers = { 5, 1, 4, 2, 3 };
var sortedNumbers = numbers.OrderBy(x => x).ToArray();
結果を配列として受け取りたい場合はToArray()を使います。
C#string[] names = { "Taro", "Hanako", "Akira" };
var sortedNames = names.OrderBy(name => name).ToArray();
LINQを使うことで、配列でもリストとほぼ同じ書き方でソートできます。
5-3. Dictionaryをキーや値でソートする
Dictionary<TKey, TValue>をソートしたい場合は、キーまたは値を指定して並べ替えます。
キーで昇順ソートする例です。
C#var scores = new Dictionary<string, int>
{
{ "Taro", 80 },
{ "Hanako", 95 },
{ "Jiro", 70 }
};
var sortedByKey = scores.OrderBy(x => x.Key);
foreach (var item in sortedByKey)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
値で降順ソートする場合は、次のように書きます。
C#var sortedByValue = scores.OrderByDescending(x => x.Value);
foreach (var item in sortedByValue)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
ランキング表示では、値を基準にした降順ソートがよく使われます。
C#var ranking = scores
.OrderByDescending(x => x.Value)
.ToList();
5-4. 日付・DateTimeをソートする
DateTimeもLINQで簡単にソートできます。
C#public class Article
{
public string Title { get; set; } = "";
public DateTime PublishedAt { get; set; }
}
古い記事から順に並べる場合は昇順です。
C#var oldFirst = articles
.OrderBy(article => article.PublishedAt)
.ToList();
新しい記事から順に並べる場合は降順です。
C#var newFirst = articles
.OrderByDescending(article => article.PublishedAt)
.ToList();
ブログ記事、ニュース、注文履歴、ログ一覧などでは、日付の降順ソートがよく使われます。
5-5. nullを含むデータを安全にソートする
nullを含むデータをソートするときは、null参照に注意が必要です。
たとえば、次のようなコードはNameがnullでも基本的にはソートできます。
C#var sortedUsers = users.OrderBy(user => user.Name);
ただし、プロパティのさらに内側を参照する場合は注意が必要です。
C#var sortedUsers = users.OrderBy(user => user.Address.City);
Addressがnullの場合、このコードは例外になります。
安全に書くには、null条件演算子を使います。
C#var sortedUsers = users.OrderBy(user => user.Address?.City);
nullを最後に並べたい場合は、次のように条件を分けるとわかりやすいです。
C#var sortedUsers = users
.OrderBy(user => user.Name == null)
.ThenBy(user => user.Name);
user.Name == nullは、nullでない場合にfalse、nullの場合にtrueになります。昇順ではfalseが先、trueが後になるため、nullを最後にできます。
5-6. 大文字・小文字を区別せず文字列をソートする
文字列をソートするとき、大文字・小文字を区別したくない場合があります。
その場合は、StringComparerを指定できます。
C#var names = new List<string> { "apple", "Banana", "cherry", "Apricot" };
var sortedNames = names.OrderBy(name => name, StringComparer.OrdinalIgnoreCase);
オブジェクトのプロパティで比較する場合も同じです。
C#var sortedUsers = users
.OrderBy(user => user.Name, StringComparer.OrdinalIgnoreCase)
.ToList();
単純にToLower()やToUpper()を使って比較する方法もありますが、文字列比較の意図を明確にするならStringComparerを使う方が読みやすくなります。
6. カスタム条件でソートする方法
LINQのソートでは、単純なプロパティだけでなく、ラムダ式、IComparer、条件式などを使って独自ルールで並べ替えできます。
業務アプリでは、「ステータスの優先順」「重要度の高い順」「未対応を先に表示」など、単純な昇順・降順では表現しにくい並び順がよくあります。
6-1. ラムダ式で並び替え条件を指定する
OrderByの中には、任意のラムダ式を書けます。
C#var sortedProducts = products
.OrderBy(product => product.Price * product.Quantity)
.ToList();
この例では、Price * Quantityを基準にソートしています。
文字列の長さで並べ替えることもできます。
C#var sortedNames = names
.OrderBy(name => name.Length)
.ToList();
ラムダ式を使えば、プロパティそのものだけでなく、計算結果や変換結果をソートキーにできます。
6-2. IComparerを使って独自ルールでソートする
より複雑な比較ルールを使いたい場合は、IComparer<T>を実装します。
C#public class LengthComparer : IComparer<string>
{
public int Compare(string? x, string? y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
}
OrderByに比較方法を渡すこともできます。
C#var sortedNames = names.OrderBy(name => name, new LengthComparer());
ただし、単純な文字列比較であれば、独自のIComparerを作るよりもStringComparerを使う方が簡潔です。
C#var sortedNames = names.OrderBy(name => name, StringComparer.OrdinalIgnoreCase);
独自の比較クラスは、業務固有のルールが複雑な場合に使うとよいでしょう。
6-3. 条件式を使って優先順位順にソートする
ステータスや種別などを特定の優先順位で並べたい場合は、条件式を使う方法が便利です。
たとえば、タスクを「未対応」「対応中」「完了」の順に並べたいとします。
C#var sortedTasks = tasks
.OrderBy(task => task.Status == "未対応" ? 1 :
task.Status == "対応中" ? 2 :
task.Status == "完了" ? 3 : 99)
.ToList();
このように、文字列の自然な順序ではなく、業務上の優先順位を数値に変換してソートできます。
より読みやすくするなら、メソッドに切り出すのがおすすめです。
C#int GetStatusRank(string status)
{
return status switch
{
"未対応" => 1,
"対応中" => 2,
"完了" => 3,
_ => 99
};
}
var sortedTasks = tasks
.OrderBy(task => GetStatusRank(task.Status))
.ToList();
6-4. 複雑な業務データを並べ替える考え方
複雑な業務データをソートするときは、いきなりコードを書くのではなく、並び順のルールを言葉で整理すると失敗しにくくなります。
たとえば、問い合わせ一覧を次のように並べたいとします。
未対応を先に表示
優先度が高いものを先に表示
期限が近いものを先に表示
作成日が古いものを先に表示
この場合は、次のように書けます。
C#var sortedInquiries = inquiries
.OrderBy(inquiry => inquiry.Status == "完了")
.ThenByDescending(inquiry => inquiry.Priority)
.ThenBy(inquiry => inquiry.Deadline)
.ThenBy(inquiry => inquiry.CreatedAt)
.ToList();
重要なのは、上から順に「優先したい条件」を並べることです。
OrderByとThenByの順番が、そのまま業務ルールの優先順位になります。
7. LINQソートでよくあるミスと注意点
LINQのソートは便利ですが、いくつか注意点があります。
特に、結果を代入し忘れる、OrderByを連続で使う、遅延実行を理解していない、null参照が起きる、といったミスはよくあります。
7-1. OrderByの結果を代入し忘れる
LINQのOrderByは元のコレクションを変更しません。
そのため、次のコードはソート結果を使っていないため意味がありません。
C#users.OrderBy(user => user.Name);
正しくは、戻り値を変数に代入します。
C#var sortedUsers = users.OrderBy(user => user.Name).ToList();
または、同じ変数に代入し直します。
C#users = users.OrderBy(user => user.Name).ToList();
LINQのメソッドは「結果を返す」ものだと覚えておくとよいでしょう。
7-2. OrderByを複数回使って並び順が上書きされる
複数条件ソートで、OrderByを何度も使ってしまうミスもよくあります。
C#var result = users
.OrderBy(user => user.Department)
.OrderBy(user => user.Name);
このコードは、「部署順にしてから名前順」ではなく、2回目のOrderByが主要なソート条件になります。
複数条件を指定する場合は、2つ目以降にThenByを使います。
C#var result = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Name);
降順を追加したい場合はThenByDescendingです。
C#var result = users
.OrderBy(user => user.Department)
.ThenByDescending(user => user.Age);
7-3. ソート結果が遅延実行される点に注意する
LINQのOrderByは遅延実行です。
つまり、OrderByを書いた時点ではすぐにソートが実行されず、foreachで列挙したり、ToList()を呼んだりしたタイミングで実行されます。
C#var sortedUsers = users.OrderBy(user => user.Name);
// この時点ではまだ列挙されていない
foreach (var user in sortedUsers)
{
Console.WriteLine(user.Name);
}
結果をその時点で確定したい場合は、ToList()やToArray()を使います。
C#var sortedUsers = users
.OrderBy(user => user.Name)
.ToList();
元データが後から変更される可能性がある場合は、早めにToList()で確定しておくと安全です。
7-4. null参照でエラーになるケース
ソートキーに指定したプロパティがnullになる可能性がある場合は注意が必要です。
次のコードでは、Addressがnullの場合に例外が発生します。
C#var result = users.OrderBy(user => user.Address.City);
安全に書くには、null条件演算子を使います。
C#var result = users.OrderBy(user => user.Address?.City);
nullを最後にしたい場合は、条件を追加します。
C#var result = users
.OrderBy(user => user.Address?.City == null)
.ThenBy(user => user.Address?.City);
nullが入り得るデータでは、「nullの場合にどこへ並べるか」を明示しておくと、仕様としてもコードとしてもわかりやすくなります。
7-5. 大量データをソートするときのパフォーマンス注意点
大量データをLINQでソートする場合、パフォーマンスにも注意が必要です。
ソートは要素数が増えるほど処理負荷が大きくなります。特に、数万件、数十万件以上のデータをアプリケーション側で並べ替える場合は、メモリ使用量や処理時間を意識する必要があります。
データベースから取得するデータであれば、可能な限りデータベース側でORDER BYを使って並べ替えるのが基本です。
Entity Frameworkを使っている場合も、ToList()の前にOrderByを書くことで、SQLのORDER BYに変換されることが多いです。
C#var users = dbContext.Users
.OrderBy(user => user.Name)
.Skip(0)
.Take(20)
.ToList();
一方、ToList()した後にOrderByを書くと、データをすべて取得した後にメモリ上で並べ替えることになります。
C#var users = dbContext.Users
.ToList()
.OrderBy(user => user.Name);
大量データでは、OrderBy、Skip、TakeをToList()より前に書くことが重要です。
8. LINQソートの書き方別比較
LINQには、メソッド構文とクエリ構文があります。
どちらでもソートは書けますが、C#の実務ではメソッド構文がよく使われます。
8-1. メソッド構文でソートする書き方
メソッド構文は、OrderByやThenByをメソッドチェーンでつなげる書き方です。
C#var result = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Name)
.ToList();
降順の場合は次のように書きます。
C#var result = users
.OrderByDescending(user => user.CreatedAt)
.ToList();
メソッド構文は、Where、Select、Skip、Takeなど他のLINQメソッドとも組み合わせやすいのが特徴です。
C#var result = users
.Where(user => user.IsActive)
.OrderBy(user => user.Name)
.Select(user => user.Name)
.ToList();
8-2. クエリ構文でソートする書き方
クエリ構文では、SQLに近い形でソートを書けます。
C#var result =
from user in users
orderby user.Name
select user;
降順にしたい場合は、descendingを付けます。
C#var result =
from user in users
orderby user.CreatedAt descending
select user;
複数条件も書けます。
C#var result =
from user in users
orderby user.Department, user.Name
select user;
一部だけ降順にする場合は、条件ごとにdescendingを指定します。
C#var result =
from user in users
orderby user.Department, user.Age descending
select user;
8-3. メソッド構文とクエリ構文の使い分け
メソッド構文は、LINQの各メソッドを細かく組み合わせたいときに向いています。
C#var result = users
.Where(user => user.IsActive)
.OrderBy(user => user.Department)
.ThenBy(user => user.Name)
.Skip(20)
.Take(10)
.ToList();
クエリ構文は、SQLに慣れている人にとって読みやすい場合があります。
C#var result =
from user in users
where user.IsActive
orderby user.Department, user.Name
select user;
どちらを使っても結果は同じように書けますが、チーム内で書き方を統一することが大切です。
実務では、ThenByやSkip、Take、Selectと組み合わせる場面が多いため、メソッド構文の方が使われることが多いです。
8-4. 可読性の高いソート処理を書くコツ
LINQソートを読みやすくするには、条件を上から優先順位順に並べることが大切です。
C#var result = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Role)
.ThenBy(user => user.Name)
.ToList();
また、複雑な条件はメソッドに切り出すと読みやすくなります。
C#var result = tasks
.OrderBy(task => GetStatusRank(task.Status))
.ThenByDescending(task => task.Priority)
.ThenBy(task => task.Deadline)
.ToList();
ソート条件が長くなりすぎる場合は、「何の順番で並べたいのか」がコードから伝わるように、変数名やメソッド名を工夫しましょう。
C#var sortedTasks = tasks
.OrderBy(task => GetStatusSortOrder(task.Status))
.ThenBy(task => task.DueDate)
.ToList();
可読性の高いLINQソートは、後から仕様変更が入ったときにも修正しやすくなります。
9. LINQソートの応用テクニック
ここからは、実務でよく使うLINQソートの応用テクニックを紹介します。
ユーザーが選んだ項目でソートする、昇順・降順を切り替える、ページングと組み合わせる、Entity Frameworkで使う、といったケースです。
9-1. 動的にソート項目を切り替える
画面上でユーザーが「名前順」「日付順」「価格順」などを選べる場合、ソート項目を動的に切り替える必要があります。
C#IEnumerable<Product> query = products;
query = sortKey switch
{
"name" => query.OrderBy(product => product.Name),
"price" => query.OrderBy(product => product.Price),
"createdAt" => query.OrderBy(product => product.CreatedAt),
_ => query.OrderBy(product => product.Name)
};
var result = query.ToList();
switch式を使うと、ソート条件を見やすく整理できます。
降順も含めたい場合は、次のように分岐できます。
C#query = sortKey switch
{
"name" => query.OrderBy(product => product.Name),
"price" => query.OrderBy(product => product.Price),
"createdAt" => query.OrderByDescending(product => product.CreatedAt),
_ => query.OrderBy(product => product.Name)
};
9-2. ユーザー指定の昇順・降順で並べ替える
ユーザーが昇順・降順を選べる場合は、boolや文字列で方向を切り替えます。
C#bool descending = true;
var result = descending
? products.OrderByDescending(product => product.Price).ToList()
: products.OrderBy(product => product.Price).ToList();
ソート項目と方向の両方を切り替えたい場合は、メソッドにまとめると便利です。
C#IEnumerable<Product> SortProducts(
IEnumerable<Product> products,
string sortKey,
bool descending)
{
return sortKey switch
{
"name" => descending
? products.OrderByDescending(product => product.Name)
: products.OrderBy(product => product.Name),
"price" => descending
? products.OrderByDescending(product => product.Price)
: products.OrderBy(product => product.Price),
"createdAt" => descending
? products.OrderByDescending(product => product.CreatedAt)
: products.OrderBy(product => product.CreatedAt),
_ => products.OrderBy(product => product.Name)
};
}
画面の一覧表示では、このようにソート処理を共通化しておくと保守しやすくなります。
9-3. ページング処理と組み合わせる
一覧画面では、ソートとページングを組み合わせることがよくあります。
基本の順番は、OrderBy、Skip、Takeです。
C#int page = 2;
int pageSize = 10;
var result = products
.OrderBy(product => product.Name)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
この例では、名前順に並べたうえで、2ページ目の10件を取得しています。
ページングでは、先に並び順を確定してからSkipとTakeを使うことが重要です。
C#var result = products
.OrderBy(product => product.CreatedAt)
.Skip(20)
.Take(10)
.ToList();
ソートしないままページングすると、表示順が不安定になることがあります。
9-4. Entity FrameworkでLINQソートを使うときの注意点
Entity FrameworkでLINQソートを使う場合は、ToList()を呼ぶ位置に注意が必要です。
次のように書くと、データベース側でソートやページングが行われます。
C#var users = dbContext.Users
.Where(user => user.IsActive)
.OrderBy(user => user.Name)
.Skip(0)
.Take(20)
.ToList();
一方、先にToList()してしまうと、その時点でデータベースから取得され、以降はメモリ上で処理されます。
C#var users = dbContext.Users
.Where(user => user.IsActive)
.ToList()
.OrderBy(user => user.Name);
件数が少ない場合は問題になりにくいですが、大量データではパフォーマンスに影響します。
Entity Frameworkでは、基本的にWhere、OrderBy、Skip、Takeを組み立ててから、最後にToList()を呼ぶようにしましょう。
9-5. OrderBy・Skip・Takeを組み合わせた一覧表示
一覧表示では、検索条件、ソート条件、ページングを組み合わせるのが一般的です。
C#int page = 1;
int pageSize = 20;
var result = dbContext.Products
.Where(product => product.IsPublished)
.OrderByDescending(product => product.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
このコードでは、公開済みの商品だけを対象に、新しい順で並べ、指定ページのデータだけを取得しています。
複数条件ソートも組み合わせられます。
C#var result = dbContext.Products
.Where(product => product.IsPublished)
.OrderBy(product => product.CategoryId)
.ThenByDescending(product => product.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
一覧表示では、毎回同じ並び順になるように、最後に一意性のあるキーを追加することもあります。
C#var result = dbContext.Products
.OrderByDescending(product => product.CreatedAt)
.ThenBy(product => product.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
同じ日付のデータが複数ある場合でも、Idを追加することで並び順が安定しやすくなります。
10. C# LINQソートのFAQ
ここでは、C# LINQのソートでよくある疑問に回答します。
10-1. LINQで元のListを直接ソートできますか?
LINQのOrderByは、元のList<T>を直接変更しません。
C#var sorted = users.OrderBy(user => user.Name).ToList();
元のリスト自体を並べ替えたい場合は、結果を代入し直します。
C#users = users.OrderBy(user => user.Name).ToList();
または、List<T>.Sortを使います。
C#users.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal));
元データを残したいならLINQ、元のリストを直接変更したいならSortを使うとよいでしょう。
10-2. OrderByとThenByの違いは何ですか?
OrderByは最初の並べ替え条件を指定するメソッドです。
C#var result = users.OrderBy(user => user.Department);
ThenByは、OrderByで並べた後に、追加の条件を指定するメソッドです。
C#var result = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Name);
複数条件ソートでは、最初にOrderBy、2つ目以降にThenByを使います。
10-3. 降順と昇順を混在させるにはどうすればよいですか?
OrderBy、OrderByDescending、ThenBy、ThenByDescendingを組み合わせます。
C#var result = users
.OrderBy(user => user.Department)
.ThenByDescending(user => user.Age);
この例では、部署は昇順、同じ部署内では年齢の降順になります。
最初の条件を降順にすることもできます。
C#var result = users
.OrderByDescending(user => user.CreatedAt)
.ThenBy(user => user.Name);
このように、条件ごとに昇順・降順を指定できます。
10-4. nullを最後に並べるにはどうすればよいですか?
nullを最後にしたい場合は、まずnullかどうかで並べ、その後に値で並べます。
C#var result = users
.OrderBy(user => user.Name == null)
.ThenBy(user => user.Name)
.ToList();
Name == nullがfalseのデータが先に来て、trueのデータが後に来るため、nullを最後にできます。
オブジェクトのネストしたプロパティを使う場合は、null条件演算子も使いましょう。
C#var result = users
.OrderBy(user => user.Address?.City == null)
.ThenBy(user => user.Address?.City)
.ToList();
10-5. List<T>.SortとLINQのOrderByはどちらを使うべきですか?
元のリストを変更したい場合はList<T>.Sort、元のリストを変更せずに並べ替え結果を取得したい場合はLINQのOrderByが向いています。
LINQのOrderByは、次のように読みやすく書けます。
C#var sortedUsers = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Name)
.ToList();
List<T>.Sortは、元のリストをその場で並べ替えます。
C#users.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal));
可読性や複数条件の書きやすさを重視するならLINQ、メモリ効率や元リストの直接変更を重視するならList<T>.Sortを検討するとよいでしょう。
まとめ
C# LINQのソートでは、昇順に並べるOrderBy、降順に並べるOrderByDescending、複数条件を追加するThenByとThenByDescendingを使います。
基本の書き方は次のとおりです。
C#var result = items.OrderBy(item => item.Name);
降順の場合は次のように書きます。
C#var result = items.OrderByDescending(item => item.CreatedAt);
複数条件で並べ替える場合は、最初にOrderBy、2つ目以降にThenByを使います。
C#var result = users
.OrderBy(user => user.Department)
.ThenBy(user => user.Name)
.ToList();
LINQのソートは元のコレクションを変更しないため、結果を使うには変数に代入したり、ToList()やToArray()で確定したりする必要があります。
また、nullを含むデータ、文字列の大文字・小文字、独自の優先順位、大量データ、Entity Frameworkでの利用など、実務では注意すべき点もあります。
C#で一覧表示やランキング、検索結果、管理画面の並べ替えを実装するなら、LINQのOrderBy、OrderByDescending、ThenBy、ThenByDescendingを正しく使い分けることが重要です。

