C#のリストをソートする方法|Listの昇順・降順・複数条件・独自クラスまで徹底解説
はじめに
C#でリストを扱うとき、データを並べ替える「ソート」は非常によく使う処理です。
たとえば、次のような場面があります。
数値のリストを小さい順に並べる
名前を五十音順やアルファベット順に並べる
更新日時が新しい順に表示する
商品を価格順に並べる
ユーザー一覧を部署順、年齢順、名前順で並べる
独自クラスのListを特定のプロパティで並べ替える
C#でリストをソートする場合、主に使うのは List<T>.Sort() と LINQ の OrderBy() / OrderByDescending() です。
ただし、これらは似ているようで大きな違いがあります。
Sort() は元のリスト自体を並べ替えるメソッドです。一方、OrderBy() は元のリストを変更せず、並べ替えた結果を新しいシーケンスとして返します。
この記事では、C#のリストをソートする方法について、基本の昇順・降順から、複数条件、独自クラス、nullの扱い、実務で使える応用例までわかりやすく解説します。
1. C#でListをソートする基本
C#で List<T> をソートする方法はいくつかあります。
まずは、よく使う方法とそれぞれの違いを整理しておきましょう。
1-1. List<T>のソートでよく使う3つの方法
C#でリストをソートするときによく使うのは、次の3つです。
C#list.Sort();
C#list.OrderBy(x => x);
C#list.OrderByDescending(x => x);
Sort() は List<T> に用意されているメソッドです。元のリストを直接並べ替えます。
OrderBy() と OrderByDescending() は LINQ のメソッドです。元のリストは変更せず、並べ替えた結果を返します。
LINQを使う場合は、次の名前空間が必要です。
C#using System.Linq;
最近のC#プロジェクトでは最初から使えることも多いですが、コンパイルエラーになる場合は using System.Linq; を追加してください。
1-2. Sort()・OrderBy()・OrderByDescending()の違い
それぞれの違いを簡単にまとめると、次のようになります。
| 方法 | 並び順 | 元のリスト | 主な用途 |
|---|---|---|---|
Sort() | 昇順が基本 | 書き換える | リスト自体を並べ替えたい |
OrderBy() | 昇順 | 書き換えない | ソート結果を別に取得したい |
OrderByDescending() | 降順 | 書き換えない | 降順の結果を別に取得したい |
たとえば、次のようなリストがあるとします。
C#var numbers = new List<int> { 3, 1, 5, 2, 4 };
Sort() を使うと、numbers 自体が並べ替わります。
C#numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 4, 5
一方、OrderBy() は元のリストを変更しません。
C#var numbers = new List<int> { 3, 1, 5, 2, 4 };
var sortedNumbers = numbers.OrderBy(x => x).ToList();
Console.WriteLine(string.Join(", ", sortedNumbers));
// 1, 2, 3, 4, 5
Console.WriteLine(string.Join(", ", numbers));
// 3, 1, 5, 2, 4
この違いは非常に重要です。
1-3. 元のリストを書き換える方法と書き換えない方法
C#のリストソートでは、「元のリストを書き換えるかどうか」を意識する必要があります。
元のリストを直接並べ替えるなら Sort() を使います。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
元のリストはそのまま残し、ソート済みの別リストを作るなら OrderBy() を使います。
C#var sortedUsers = users.OrderBy(x => x.Age).ToList();
実務では、画面表示用に並べ替えるだけなら OrderBy()、内部データの順序そのものを変更してよいなら Sort() を使うことが多いです。
1-4. まず結論:用途別に選ぶソート方法
C#でリストをソートするときは、次のように選ぶとわかりやすいです。
| やりたいこと | おすすめの方法 |
|---|---|
| 数値や文字列のListを昇順にしたい | Sort() |
| 元のListを変更したくない | OrderBy() |
| 降順にしたい | OrderByDescending() |
| 複数条件でソートしたい | OrderBy().ThenBy() |
| 独自クラスをプロパティで並べ替えたい | OrderBy(x => x.Property) |
| 独自の比較ルールを使いたい | Sort((a, b) => ...) または IComparer<T> |
| リスト自体を高速に並べ替えたい | Sort() |
| 読みやすさを優先したい | LINQ |
迷った場合は、まず OrderBy() を使うと安全です。元のリストを書き換えないため、副作用が少なく、コードの意図もわかりやすいからです。
2. List<T>.Sort()で昇順にソートする方法
List<T>.Sort() は、C#でリストをソートするもっとも基本的な方法です。
数値、文字列、DateTime など、標準で比較できる型であれば、そのまま Sort() を呼び出すだけで昇順に並び替えられます。
2-1. 数値のListを昇順に並べ替える
整数のリストを昇順にソートする例です。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 5, 8
int は標準で大小比較ができる型なので、Sort() を呼ぶだけで小さい順に並びます。
double や decimal でも同じように使えます。
C#var prices = new List<decimal> { 1200m, 500m, 980m, 3000m };
prices.Sort();
Console.WriteLine(string.Join(", ", prices));
// 500, 980, 1200, 3000
価格、点数、件数、IDなどを小さい順に並べたい場合に便利です。
2-2. 文字列のListを昇順に並べ替える
文字列のリストも Sort() でソートできます。
C#var names = new List<string> { "Taro", "Hanako", "Jiro", "Akira" };
names.Sort();
Console.WriteLine(string.Join(", ", names));
// Akira, Hanako, Jiro, Taro
アルファベット順に並びます。
日本語の文字列もソートできます。
C#var fruits = new List<string> { "みかん", "りんご", "いちご", "ぶどう" };
fruits.Sort();
Console.WriteLine(string.Join(", ", fruits));
ただし、日本語の並び順は期待する五十音順と完全に一致しない場合があります。日本語や大文字小文字を含む文字列ソートについては、後の注意点で詳しく解説します。
2-3. DateTimeのListを日付順に並べ替える
DateTime のリストも Sort() で日付の古い順に並べ替えられます。
C#var dates = new List<DateTime>
{
new DateTime(2024, 5, 1),
new DateTime(2023, 12, 10),
new DateTime(2024, 1, 15)
};
dates.Sort();
foreach (var date in dates)
{
Console.WriteLine(date.ToString("yyyy/MM/dd"));
}
出力例は次のようになります。
C#2023/12/10
2024/01/15
2024/05/01
更新日時、登録日、締切日などを古い順に表示したい場合に使えます。
2-4. Sort()実行後に元のリストがどう変わるか
Sort() の重要なポイントは、元のリスト自体を変更することです。
C#var numbers = new List<int> { 3, 1, 2 };
numbers.Sort();
Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3
この時点で、numbers の中身は { 1, 2, 3 } に変わっています。
次のように、元の順番を後で使いたい場合には注意が必要です。
C#var original = new List<int> { 3, 1, 2 };
var sorted = new List<int>(original);
sorted.Sort();
Console.WriteLine(string.Join(", ", original));
// 3, 1, 2
Console.WriteLine(string.Join(", ", sorted));
// 1, 2, 3
元のリストを残したい場合は、コピーしてから Sort() するか、LINQの OrderBy() を使いましょう。
3. List<T>を降順にソートする方法
C#でリストを降順にソートする方法はいくつかあります。
代表的な方法は次の3つです。
Sort()のあとにReverse()するOrderByDescending()を使う比較処理を逆に書く
それぞれ見ていきましょう。
3-1. Sort()とReverse()を組み合わせて降順にする
一番シンプルなのは、まず昇順にソートしてから、順番を反転する方法です。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
numbers.Sort();
numbers.Reverse();
Console.WriteLine(string.Join(", ", numbers));
// 8, 5, 3, 2, 1
Sort() で小さい順に並べたあと、Reverse() で逆順にしています。
文字列でも同じです。
C#var names = new List<string> { "Taro", "Hanako", "Jiro", "Akira" };
names.Sort();
names.Reverse();
Console.WriteLine(string.Join(", ", names));
// Taro, Jiro, Hanako, Akira
簡単ですが、昇順ソートと反転の2ステップになるため、条件が複雑な場合は少し読みづらくなります。
3-2. OrderByDescending()で降順に並べ替える
LINQを使うなら、OrderByDescending() が便利です。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
var sortedNumbers = numbers.OrderByDescending(x => x).ToList();
Console.WriteLine(string.Join(", ", sortedNumbers));
// 8, 5, 3, 2, 1
OrderByDescending(x => x) は、「要素そのものを基準に降順で並べる」という意味です。
独自クラスのプロパティでも使いやすいです。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 35 }
};
var sortedUsers = users
.OrderByDescending(x => x.Age)
.ToList();
この例では、年齢が高い順に並びます。
3-3. CompareToを逆にして降順ソートする
Sort() にラムダ式を渡して、比較条件を逆にすることもできます。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
numbers.Sort((a, b) => b.CompareTo(a));
Console.WriteLine(string.Join(", ", numbers));
// 8, 5, 3, 2, 1
通常の昇順では次のように書きます。
C#numbers.Sort((a, b) => a.CompareTo(b));
降順では a と b を逆にします。
C#numbers.Sort((a, b) => b.CompareTo(a));
独自クラスでも使えます。
C#users.Sort((a, b) => b.Age.CompareTo(a.Age));
これは年齢の降順ソートです。
3-4. 降順ソートで使い分けたい書き方
降順ソートでは、状況に応じて書き方を選ぶとよいです。
元のリストを変更してよい場合は、Sort() と Reverse() または比較ラムダを使います。
C#numbers.Sort();
numbers.Reverse();
C#numbers.Sort((a, b) => b.CompareTo(a));
元のリストを変更したくない場合は、OrderByDescending() を使います。
C#var sorted = numbers.OrderByDescending(x => x).ToList();
独自クラスのプロパティで降順にしたい場合も、OrderByDescending() が読みやすいです。
C#var sortedUsers = users.OrderByDescending(x => x.Age).ToList();
実務では、可読性を重視して OrderByDescending() を使う場面が多いです。
4. LINQのOrderByでリストをソートする方法
LINQの OrderBy() は、C#でリストをソートするときに非常によく使われます。
特に、次のような場面で便利です。
元のリストを変更したくない
独自クラスのプロパティで並べ替えたい
複数条件でソートしたい
コードを読みやすく書きたい
4-1. OrderBy()で昇順にソートする
数値のリストを OrderBy() で昇順にソートする例です。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
var sortedNumbers = numbers.OrderBy(x => x).ToList();
Console.WriteLine(string.Join(", ", sortedNumbers));
// 1, 2, 3, 5, 8
x => x は、要素そのものをソートキーとして使うという意味です。
文字列でも同じように書けます。
C#var names = new List<string> { "Taro", "Hanako", "Jiro", "Akira" };
var sortedNames = names.OrderBy(x => x).ToList();
Console.WriteLine(string.Join(", ", sortedNames));
// Akira, Hanako, Jiro, Taro
4-2. OrderByDescending()で降順にソートする
降順に並べる場合は OrderByDescending() を使います。
C#var numbers = new List<int> { 5, 2, 8, 1, 3 };
var sortedNumbers = numbers.OrderByDescending(x => x).ToList();
Console.WriteLine(string.Join(", ", sortedNumbers));
// 8, 5, 3, 2, 1
日付を新しい順にしたい場合にもよく使います。
C#var dates = new List<DateTime>
{
new DateTime(2024, 5, 1),
new DateTime(2023, 12, 10),
new DateTime(2024, 1, 15)
};
var sortedDates = dates
.OrderByDescending(x => x)
.ToList();
これで日付が新しい順に並びます。
4-3. ToList()でソート結果をList<T>に戻す
OrderBy() の戻り値は List<T> ではありません。
C#var sorted = numbers.OrderBy(x => x);
この sorted は、厳密には IOrderedEnumerable<T> です。
List<T> として使いたい場合は、最後に ToList() を付けます。
C#var sortedList = numbers.OrderBy(x => x).ToList();
独自クラスでも同じです。
C#var sortedUsers = users
.OrderBy(x => x.Name)
.ToList();
画面表示や後続処理で List<T> が必要な場合は、ToList() を忘れないようにしましょう。
4-4. Sort()とOrderBy()はどちらを使うべきか
Sort() と OrderBy() のどちらを使うべきかは、目的によって変わります。
元のリストを変更したいなら Sort() です。
C#numbers.Sort();
元のリストを変更したくないなら OrderBy() です。
C#var sortedNumbers = numbers.OrderBy(x => x).ToList();
独自クラスをプロパティ指定でソートしたい場合は、LINQのほうが直感的です。
C#var sortedUsers = users.OrderBy(x => x.Age).ToList();
一方、リスト自体をその場で並べ替えるなら Sort() が適しています。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
実務では、次のように考えるとよいでしょう。
データそのものの順番を変えるなら
Sort()表示用や一時的な並び替えなら
OrderBy()複数条件なら
OrderBy().ThenBy()可読性重視なら LINQ
破壊的変更を避けたいなら LINQ
5. 複数条件でListをソートする方法
C#でリストを複数条件でソートする場合は、LINQの ThenBy() と ThenByDescending() を使うのが一般的です。
たとえば、次のようなユーザー一覧があるとします。
C#public class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
public string Department { get; set; } = "";
}
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30, Department = "Sales" },
new User { Name = "Hanako", Age = 25, Department = "Sales" },
new User { Name = "Jiro", Age = 30, Department = "Dev" },
new User { Name = "Akira", Age = 25, Department = "Dev" }
};
このリストを部署順、年齢順、名前順などで並べ替える例を見ていきましょう。
5-1. ThenBy()で第2条件を指定する
第1条件を OrderBy()、第2条件を ThenBy() で指定します。
C#var sortedUsers = users
.OrderBy(x => x.Department)
.ThenBy(x => x.Age)
.ToList();
このコードは、まず Department で昇順に並べ、同じ部署の中では Age の昇順で並べます。
たとえば、部署ごとにまとめて、その中で年齢が若い順に表示したい場合に使えます。
5-2. ThenByDescending()で一部の条件だけ降順にする
一部の条件だけ降順にしたい場合は、ThenByDescending() を使います。
C#var sortedUsers = users
.OrderBy(x => x.Department)
.ThenByDescending(x => x.Age)
.ToList();
この場合、部署は昇順、同じ部署の中では年齢が高い順になります。
第1条件を降順にすることもできます。
C#var sortedUsers = users
.OrderByDescending(x => x.Department)
.ThenBy(x => x.Age)
.ToList();
このように、条件ごとに昇順・降順を細かく指定できます。
5-3. 3つ以上の条件でソートする
3つ以上の条件でソートしたい場合は、ThenBy() を続けて書きます。
C#var sortedUsers = users
.OrderBy(x => x.Department)
.ThenBy(x => x.Age)
.ThenBy(x => x.Name)
.ToList();
このコードでは、次の順番でソートされます。
部署の昇順
年齢の昇順
名前の昇順
一部を降順にすることもできます。
C#var sortedUsers = users
.OrderBy(x => x.Department)
.ThenByDescending(x => x.Age)
.ThenBy(x => x.Name)
.ToList();
部署順に並べ、同じ部署では年齢が高い順、同じ年齢なら名前順になります。
5-4. 複数条件ソートの実用例
実務では、複数条件ソートは非常によく使います。
たとえば、商品一覧をカテゴリ順、価格順、商品名順で並べる例です。
C#public class Product
{
public int Id { get; set; }
public string Category { get; set; } = "";
public string Name { get; set; } = "";
public decimal Price { get; set; }
public DateTime UpdatedAt { get; set; }
}
C#var sortedProducts = products
.OrderBy(x => x.Category)
.ThenBy(x => x.Price)
.ThenBy(x => x.Name)
.ToList();
カテゴリごとにまとめ、同じカテゴリ内では価格が安い順、同じ価格なら商品名順に並びます。
更新日時が新しい順にしたい場合は、次のように書きます。
C#var sortedProducts = products
.OrderBy(x => x.Category)
.ThenByDescending(x => x.UpdatedAt)
.ToList();
このように、OrderBy() と ThenBy() を組み合わせることで、実務的な並び替えを簡単に表現できます。
6. 独自クラスのListをソートする方法
C#では、List<int> や List<string> だけでなく、自分で作ったクラスのリストをソートすることも多いです。
たとえば、ユーザー一覧、商品一覧、注文一覧、タスク一覧などです。
独自クラスをソートする場合は、「どのプロパティを基準に並べるか」を指定する必要があります。
6-1. クラスのプロパティを指定してソートする
次のような User クラスがあるとします。
C#public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
}
この User のリストを年齢順に並べる場合は、OrderBy() を使うと簡単です。
C#var users = new List<User>
{
new User { Id = 3, Name = "Taro", Age = 30 },
new User { Id = 1, Name = "Hanako", Age = 25 },
new User { Id = 2, Name = "Jiro", Age = 35 }
};
var sortedUsers = users
.OrderBy(x => x.Age)
.ToList();
名前順に並べたい場合は、Name を指定します。
C#var sortedUsers = users
.OrderBy(x => x.Name)
.ToList();
ID順に並べたい場合は、Id を指定します。
C#var sortedUsers = users
.OrderBy(x => x.Id)
.ToList();
独自クラスのリストでは、この書き方がもっともわかりやすく、実務でもよく使われます。
6-2. ラムダ式で比較条件を書く
Sort() にラムダ式を渡して、独自クラスをソートすることもできます。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
これは、Age の昇順で元のリストを並べ替えるコードです。
名前順なら次のように書けます。
C#users.Sort((a, b) => string.Compare(a.Name, b.Name));
降順にしたい場合は、比較の向きを逆にします。
C#users.Sort((a, b) => b.Age.CompareTo(a.Age));
Sort() を使うと元のリスト自体が変更されます。
C#users.Sort((a, b) => a.Id.CompareTo(b.Id));
元のリストを書き換えてよい場合には、シンプルで便利です。
6-3. IComparable<T>を実装してSort()できるようにする
独自クラスに標準の並び順を持たせたい場合は、IComparable<T> を実装します。
C#public class User : IComparable<User>
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int Age { get; set; }
public int CompareTo(User? other)
{
if (other == null)
{
return 1;
}
return Age.CompareTo(other.Age);
}
}
このようにすると、Sort() を引数なしで呼べるようになります。
C#var users = new List<User>
{
new User { Id = 3, Name = "Taro", Age = 30 },
new User { Id = 1, Name = "Hanako", Age = 25 },
new User { Id = 2, Name = "Jiro", Age = 35 }
};
users.Sort();
この例では、Age の昇順で並びます。
ただし、IComparable<T> は「そのクラスの標準的な並び順」を定義するものです。
状況によって並び順を変えたい場合は、OrderBy() や IComparer<T> を使ったほうが柔軟です。
6-4. IComparer<T>を使って比較ロジックを分離する
比較処理をクラスの外に分離したい場合は、IComparer<T> を使います。
C#public class UserAgeComparer : IComparer<User>
{
public int Compare(User? x, User? y)
{
if (x == null && y == null)
{
return 0;
}
if (x == null)
{
return -1;
}
if (y == null)
{
return 1;
}
return x.Age.CompareTo(y.Age);
}
}
この比較クラスを使って、次のようにソートできます。
C#users.Sort(new UserAgeComparer());
名前順の比較クラスを別に用意することもできます。
C#public class UserNameComparer : IComparer<User>
{
public int Compare(User? x, User? y)
{
return string.Compare(x?.Name, y?.Name);
}
}
C#users.Sort(new UserNameComparer());
IComparer<T> を使うと、比較ロジックを再利用しやすくなります。
たとえば、複数の画面や複数の処理で同じ並び順を使う場合に便利です。
6-5. 独自クラスを複数条件でソートする
独自クラスを複数条件でソートする場合は、LINQが特に便利です。
C#var sortedUsers = users
.OrderBy(x => x.Age)
.ThenBy(x => x.Name)
.ToList();
これは、年齢の昇順で並べ、同じ年齢なら名前順に並べるコードです。
部署、役職、更新日時などを組み合わせることもできます。
C#var sortedUsers = users
.OrderBy(x => x.Department)
.ThenByDescending(x => x.UpdatedAt)
.ThenBy(x => x.Name)
.ToList();
Sort() でも複数条件は書けますが、やや複雑になります。
C#users.Sort((a, b) =>
{
int result = a.Age.CompareTo(b.Age);
if (result != 0)
{
return result;
}
return string.Compare(a.Name, b.Name);
});
可読性を考えると、複数条件のソートでは OrderBy() と ThenBy() を使うのがおすすめです。
7. ソート処理でよくあるエラーと注意点
C#でリストをソートするときは、いくつか注意点があります。
特に、独自クラス、null、文字列、元リストの変更、安定ソートの違いはつまずきやすいポイントです。
7-1. 「比較できない型」でSort()すると起きるエラー
List<T>.Sort() は、要素同士を比較できる必要があります。
たとえば、独自クラスで何も比較ルールを指定せずに Sort() を呼ぶと、実行時にエラーになることがあります。
C#public class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 }
};
users.Sort();
この場合、User 同士をどう比較すればよいかC#にはわかりません。
対策としては、次のいずれかを使います。
C#users.Sort((a, b) => a.Age.CompareTo(b.Age));
または、
C#var sortedUsers = users.OrderBy(x => x.Age).ToList();
独自クラスでは、どのプロパティでソートするかを明示するのが基本です。
7-2. nullを含むリストをソートする場合の注意
リストに null が含まれる場合、比較処理でエラーになることがあります。
たとえば、次のようなコードには注意が必要です。
C#var names = new List<string?> { "Taro", null, "Hanako" };
var sortedNames = names.OrderBy(x => x).ToList();
単純な文字列の場合は扱えることもありますが、独自クラスのプロパティにアクセスするときは NullReferenceException の原因になります。
C#var sortedUsers = users.OrderBy(x => x.Name).ToList();
もし users の中に null が含まれていると、x.Name の時点でエラーになります。
対策としては、nullを除外する方法があります。
C#var sortedUsers = users
.Where(x => x != null)
.OrderBy(x => x!.Name)
.ToList();
nullを最後に並べたい場合は、次のように書けます。
C#var sortedUsers = users
.OrderBy(x => x == null)
.ThenBy(x => x?.Name)
.ToList();
x == null は、false が先、true が後になるため、nullが最後に並びます。
nullを先頭にしたい場合は、逆にします。
C#var sortedUsers = users
.OrderByDescending(x => x == null)
.ThenBy(x => x?.Name)
.ToList();
nullを含む可能性があるリストでは、ソート前に方針を決めておくことが大切です。
7-3. OrderBy()だけでは元のリストが変わらない
C#のリストソートでよくある勘違いが、OrderBy() を呼べば元のリストが並び替わると思ってしまうことです。
次のコードを見てください。
C#var numbers = new List<int> { 3, 1, 2 };
numbers.OrderBy(x => x);
Console.WriteLine(string.Join(", ", numbers));
// 3, 1, 2
OrderBy() を呼んでいますが、元の numbers は変わっていません。
ソート結果を使うには、戻り値を受け取る必要があります。
C#var sortedNumbers = numbers.OrderBy(x => x).ToList();
元の変数に代入し直すこともできます。
C#numbers = numbers.OrderBy(x => x).ToList();
OrderBy() は元のリストを変更しない、という点を必ず覚えておきましょう。
7-4. 日本語や大文字小文字を含む文字列ソートの注意
文字列のソートでは、単純な Sort() や OrderBy() だけでは期待通りの順番にならないことがあります。
たとえば、大文字小文字が混ざっている場合です。
C#var names = new List<string> { "apple", "Banana", "cherry", "Apricot" };
var sorted = names.OrderBy(x => x).ToList();
大文字と小文字の扱いによって、期待と違う順番になることがあります。
大文字小文字を無視して並べたい場合は、StringComparer を使います。
C#var sorted = names
.OrderBy(x => x, StringComparer.OrdinalIgnoreCase)
.ToList();
Sort() でも指定できます。
C#names.Sort(StringComparer.OrdinalIgnoreCase);
日本語の並び順についても注意が必要です。
C#var words = new List<string> { "あお", "いえ", "アイス", "うみ" };
var sorted = words.OrderBy(x => x).ToList();
ひらがな、カタカナ、漢字が混在する場合、期待する五十音順にならないことがあります。
文化圏を意識した比較をしたい場合は、CultureInfo を使う方法があります。
C#using System.Globalization;
C#var comparer = StringComparer.Create(
new CultureInfo("ja-JP"),
ignoreCase: true
);
words.Sort(comparer);
文字列ソートでは、「何を自然な順番とみなすか」が要件によって異なります。検索機能や一覧表示では、仕様を確認してから実装しましょう。
7-5. 安定ソートと非安定ソートの違い
ソートには「安定ソート」と「非安定ソート」という考え方があります。
安定ソートとは、ソートキーが同じ要素同士の元の順番が保たれるソートです。
たとえば、次のようなデータがあるとします。
C#var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Jiro", Age = 25 },
new User { Name = "Hanako", Age = 30 }
};
年齢でソートしたとき、Taro と Hanako はどちらも30歳です。
安定ソートでは、元の順番である Taro → Hanako が保たれます。
LINQの OrderBy() は安定ソートです。
C#var sortedUsers = users.OrderBy(x => x.Age).ToList();
一方、List<T>.Sort() は安定ソートであることを保証しません。
同じキーの順番を保ちたい場合は、OrderBy() を使うか、明示的に第2条件を指定しましょう。
C#var sortedUsers = users
.OrderBy(x => x.Age)
.ThenBy(x => x.Name)
.ToList();
実務では、同じ値の要素がある場合に順番が重要になることがあります。特に、画面表示や帳票出力では注意しましょう。
8. 実務で使えるListソートの応用例
ここからは、実務でよく使うC#のリストソート例を紹介します。
単純な昇順・降順だけでなく、業務ロジックに合わせた並び替えを行う場面は多くあります。
8-1. ID順・名前順・更新日時順に並べ替える
エンティティやDTOのリストでは、ID順、名前順、更新日時順がよく使われます。
C#public class Item
{
public int Id { get; set; }
public string Name { get; set; } = "";
public DateTime UpdatedAt { get; set; }
}
ID順に並べる場合です。
C#var sortedById = items
.OrderBy(x => x.Id)
.ToList();
名前順に並べる場合です。
C#var sortedByName = items
.OrderBy(x => x.Name)
.ToList();
更新日時が新しい順に並べる場合です。
C#var sortedByUpdatedAt = items
.OrderByDescending(x => x.UpdatedAt)
.ToList();
管理画面では、更新日時の降順がよく使われます。新しく更新されたデータを上に表示できるためです。
8-2. 優先度順や独自ルールで並べ替える
業務アプリでは、「高」「中」「低」のような優先度順で並べたいことがあります。
C#public class TaskItem
{
public string Title { get; set; } = "";
public string Priority { get; set; } = "";
}
文字列のままソートすると、期待する順番にならない場合があります。
そこで、優先度を数値に変換してソートします。
C#var sortedTasks = tasks
.OrderBy(x => GetPriorityOrder(x.Priority))
.ToList();
int GetPriorityOrder(string priority)
{
return priority switch
{
"高" => 1,
"中" => 2,
"低" => 3,
_ => 99
};
}
このようにすると、「高」→「中」→「低」の順に並べられます。
ステータス順でも同じ考え方が使えます。
C#int GetStatusOrder(string status)
{
return status switch
{
"未対応" => 1,
"対応中" => 2,
"完了" => 3,
_ => 99
};
}
独自ルールのソートでは、ソートキーを作る関数を用意すると読みやすくなります。
8-3. 条件に一致する要素を先頭に並べる
条件に一致する要素を先頭に持ってきたい場合も、OrderBy() で表現できます。
たとえば、重要なタスクを先頭に並べる場合です。
C#public class TaskItem
{
public string Title { get; set; } = "";
public bool IsImportant { get; set; }
public DateTime DueDate { get; set; }
}
C#var sortedTasks = tasks
.OrderByDescending(x => x.IsImportant)
.ThenBy(x => x.DueDate)
.ToList();
bool の場合、false より true のほうが大きい値として扱われます。
そのため、OrderByDescending(x => x.IsImportant) と書くと、true の要素が先頭に来ます。
期限切れのタスクを先頭にしたい場合は、次のように書けます。
C#var today = DateTime.Today;
var sortedTasks = tasks
.OrderByDescending(x => x.DueDate < today)
.ThenBy(x => x.DueDate)
.ToList();
このように、条件式そのものをソートキーにできます。
8-4. Unityや業務アプリで使うリストソート例
UnityでもC#のリストソートはよく使います。
たとえば、キャラクターをスコア順に並べる例です。
C#public class Player
{
public string Name { get; set; } = "";
public int Score { get; set; }
}
C#var ranking = players
.OrderByDescending(x => x.Score)
.ToList();
スコアが高い順にランキング表示できます。
距離が近い順に敵を並べる例です。
C#var sortedEnemies = enemies
.OrderBy(x => Vector3.Distance(player.transform.position, x.transform.position))
.ToList();
業務アプリでは、一覧画面で次のようなソートがよく使われます。
C#var sortedOrders = orders
.OrderByDescending(x => x.OrderDate)
.ThenBy(x => x.CustomerName)
.ToList();
注文日が新しい順に並べ、同じ日付なら顧客名順に並べる例です。
また、承認待ちを先頭に出すようなソートもよくあります。
C#var sortedRequests = requests
.OrderByDescending(x => x.Status == "承認待ち")
.ThenBy(x => x.CreatedAt)
.ToList();
このように、C#のリストソートはゲーム開発、Webアプリ、業務システムなど幅広い場面で活用できます。
9. C#のリストソートに関するよくある質問
最後に、C#のリストソートでよくある質問をまとめます。
9-1. Listと配列のソート方法は何が違う?
List<T> の場合は Sort() を使います。
C#var list = new List<int> { 3, 1, 2 };
list.Sort();
配列の場合は Array.Sort() を使います。
C#int[] array = { 3, 1, 2 };
Array.Sort(array);
どちらも元のデータを直接並べ替えます。
LINQを使えば、Listでも配列でも同じようにソートできます。
C#var sortedList = list.OrderBy(x => x).ToList();
C#var sortedArray = array.OrderBy(x => x).ToArray();
結果をListにしたいなら ToList()、配列にしたいなら ToArray() を使います。
9-2. Sort()とOrderBy()はどちらが速い?
一般的には、元の List<T> をそのまま並べ替えるだけなら Sort() のほうが余計なリスト生成が少なく効率的です。
C#list.Sort();
一方、OrderBy() は元のリストを変更せず、新しい並び順の結果を作ります。
C#var sorted = list.OrderBy(x => x).ToList();
そのため、単純にリスト自体を並べ替えたいなら Sort()、元のリストを残したいなら OrderBy() と考えるのがよいです。
ただし、実務では速度だけでなく、可読性や安全性も重要です。
大量データでパフォーマンスが問題になる場合は計測が必要ですが、通常の一覧表示程度であれば、わかりやすいコードを優先して問題ないことが多いです。
9-3. ソートした結果を別のリストに保存するには?
ソート結果を別のリストに保存したい場合は、OrderBy() と ToList() を使います。
C#var numbers = new List<int> { 3, 1, 2 };
var sortedNumbers = numbers
.OrderBy(x => x)
.ToList();
この場合、元の numbers は変更されません。
Sort() を使いたい場合は、元のリストをコピーしてから並べ替えます。
C#var numbers = new List<int> { 3, 1, 2 };
var sortedNumbers = new List<int>(numbers);
sortedNumbers.Sort();
元リストを残したいなら、このどちらかの方法を使いましょう。
9-4. 降順と複数条件を同時に指定できる?
できます。
LINQを使うと、降順と複数条件を簡単に組み合わせられます。
C#var sortedUsers = users
.OrderByDescending(x => x.Age)
.ThenBy(x => x.Name)
.ToList();
この例では、年齢が高い順に並べ、同じ年齢なら名前順に並べます。
第2条件を降順にすることもできます。
C#var sortedUsers = users
.OrderBy(x => x.Department)
.ThenByDescending(x => x.Age)
.ThenBy(x => x.Name)
.ToList();
条件ごとに OrderBy、OrderByDescending、ThenBy、ThenByDescending を使い分ければ、柔軟なソートができます。
9-5. DictionaryやObservableCollectionもソートできる?
Dictionary<TKey, TValue> は、基本的にキーと値の対応を管理するコレクションであり、List<T> のように順序を直接並べ替えるものではありません。
ただし、LINQを使って並べ替えた結果を取得できます。
キー順に並べる例です。
C#var sortedByKey = dictionary
.OrderBy(x => x.Key)
.ToList();
値順に並べる例です。
C#var sortedByValue = dictionary
.OrderBy(x => x.Value)
.ToList();
必要であれば、並べ替えた結果から新しいDictionaryを作ることもできます。
C#var sortedDictionary = dictionary
.OrderBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Value);
ただし、Dictionaryは本来「順序付きの一覧表示」を目的とした型ではないため、表示順が重要な場合はListに変換して扱うほうがわかりやすいです。
ObservableCollection<T> の場合も、直接 Sort() はありません。
ソートしたい場合は、いったんLINQで並べ替えて作り直します。
C#var sorted = new ObservableCollection<User>(
users.OrderBy(x => x.Name)
);
WPFやMAUIなどのUIで使っている場合は、コレクションを作り直す方法のほか、ビュー側のソート機能を使う方法もあります。
まとめ
C#でリストをソートする方法は、主に Sort()、OrderBy()、OrderByDescending() の3つです。
List<T>.Sort() は元のリストを直接並べ替えます。数値、文字列、DateTime などの基本的な型であれば、引数なしで昇順にソートできます。
C#numbers.Sort();
元のリストを変更したくない場合は、LINQの OrderBy() を使います。
C#var sortedNumbers = numbers.OrderBy(x => x).ToList();
降順にしたい場合は、OrderByDescending() が便利です。
C#var sortedNumbers = numbers.OrderByDescending(x => x).ToList();
複数条件でソートする場合は、ThenBy() や ThenByDescending() を組み合わせます。
C#var sortedUsers = users
.OrderBy(x => x.Department)
.ThenByDescending(x => x.Age)
.ThenBy(x => x.Name)
.ToList();
独自クラスのリストをソートする場合は、どのプロパティで並べるかを明示するのが基本です。
C#var sortedUsers = users.OrderBy(x => x.Age).ToList();
元のリストを変更してよいなら Sort()、変更したくないなら OrderBy()、複数条件なら OrderBy().ThenBy() と覚えておくと、C#のリストソートを迷わず使い分けられます。
C#で List<T> を扱う実務では、ソート処理はほぼ必ず登場します。昇順・降順だけでなく、複数条件、独自クラス、null、文字列比較などの注意点も押さえておくことで、読みやすく安全なコードを書けるようになります。

