C# List.Sortの使い方を完全解説|昇順・降順・自作クラスの並び替えまでサンプル付き

はじめに

C#でリストの中身を並び替えたいときによく使うのが、List<T>Sortメソッドです。

たとえば、数値を小さい順に並べる、文字列をアルファベット順にする、商品価格を安い順にする、ユーザー一覧を年齢順に並べる、といった処理で使われます。

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

numbers.Sort();

Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 4, 5

List.Sortはシンプルに使える一方で、自作クラスを並び替える場合や、降順・複数条件・nullを含むListを扱う場合には注意点もあります。

この記事では、c# list sortで知りたい基本から実践的な使い方まで、サンプル付きで解説します。

1. C#のList.Sortとは?できることと基本イメージ

1-1. List.SortはList<T>の要素をその場で並び替えるメソッド

List.Sortは、List<T>に入っている要素を並び替えるためのメソッドです。

重要なのは、Sortは元のList自体の順番を変更するという点です。

C#
var numbers = new List<int> { 30, 10, 20 };

numbers.Sort();

Console.WriteLine(string.Join(", ", numbers));
// 10, 20, 30

この例では、numbersの中身そのものが並び替わっています。

Sortは戻り値を返しません。つまり、次のようには書けません。

C#
// エラーになる
var sortedNumbers = numbers.Sort();

Sortの戻り値はvoidなので、並び替えた後のListをそのまま使います。

C#
numbers.Sort();

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

1-2. 昇順・降順・自作クラスの並び替えに使える

List.Sortでは、次のような並び替えができます。

数値を昇順に並べる例です。

C#
var scores = new List<int> { 80, 100, 60, 90 };

scores.Sort();

Console.WriteLine(string.Join(", ", scores));
// 60, 80, 90, 100

文字列を並べる例です。

C#
var names = new List<string> { "Taro", "Hanako", "Jiro" };

names.Sort();

Console.WriteLine(string.Join(", ", names));
// Hanako, Jiro, Taro

降順にしたい場合は、Sort後にReverseを使う方法があります。

C#
scores.Sort();
scores.Reverse();

Console.WriteLine(string.Join(", ", scores));
// 100, 90, 80, 60

また、自作クラスのListも、並び替え条件を指定すればSortできます。

C#
var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};

users.Sort((a, b) => a.Age.CompareTo(b.Age));

foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}

// Jiro: 20
// Hanako: 25
// Taro: 30
C#
class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}

1-3. 配列のArray.SortやLINQのOrderByとの違い

C#には、List.Sort以外にも並び替えの方法があります。

配列を並び替える場合はArray.Sortを使います。

C#
int[] numbers = { 3, 1, 2 };

Array.Sort(numbers);

Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3

Array.Sortは配列用、List.SortList<T>用です。

一方、LINQのOrderByは、元のListを変更せずに、並び替えた結果を返します。

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

var sortedNumbers = numbers.OrderBy(x => x).ToList();

Console.WriteLine(string.Join(", ", numbers));
// 3, 1, 2

Console.WriteLine(string.Join(", ", sortedNumbers));
// 1, 2, 3

List.Sortは元のListを変更しますが、OrderByは元のListを変更しません。

使い分けの目安は次のとおりです。

C#
// 元のListを直接並び替えたい
numbers.Sort();

// 元のListはそのままで、並び替えた結果がほしい
var sorted = numbers.OrderBy(x => x).ToList();

2. List.Sortの基本的な使い方

2-1. 数値リストを昇順に並び替えるサンプル

数値のListを昇順、つまり小さい順に並べる場合は、Sort()を呼び出すだけです。

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

numbers.Sort();

Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 5, 8

intdoubleなどの数値型は、標準で比較ルールを持っているため、条件を指定しなくても並び替えられます。

C#
var prices = new List<decimal> { 1200m, 500m, 980m };

prices.Sort();

Console.WriteLine(string.Join(", ", prices));
// 500, 980, 1200

2-2. 文字列リストをアルファベット順に並び替えるサンプル

文字列のListも、Sort()だけで並び替えできます。

C#
var fruits = new List<string> { "banana", "apple", "orange", "grape" };

fruits.Sort();

Console.WriteLine(string.Join(", ", fruits));
// apple, banana, grape, orange

大文字・小文字が混在している場合は、期待した順番と異なることがあります。

C#
var words = new List<string> { "apple", "Banana", "orange", "Grape" };

words.Sort();

Console.WriteLine(string.Join(", ", words));

大文字・小文字を無視して並び替えたい場合は、比較ルールを指定します。

C#
words.Sort(StringComparer.OrdinalIgnoreCase);

Console.WriteLine(string.Join(", ", words));

日本語文字列を扱う場合も、標準の比較順がそのまま使われます。

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

names.Sort();

Console.WriteLine(string.Join(", ", names));

文字列の並び順を厳密に制御したい場合は、StringComparerやカルチャを意識した比較を使うことがあります。

2-3. Sort後は元のList自体の順番が変わる点に注意

List.Sortで特に注意したいのは、元のList自体が変更されることです。

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

numbers.Sort();

Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3

並び替え前の順番を後で使いたい場合は、コピーを作ってからSortします。

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

var sorted = new List<int>(original);
sorted.Sort();

Console.WriteLine("original: " + string.Join(", ", original));
// original: 3, 1, 2

Console.WriteLine("sorted: " + string.Join(", ", sorted));
// sorted: 1, 2, 3

または、LINQのOrderByを使う方法もあります。

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

var sorted = original.OrderBy(x => x).ToList();

元の順番を残したいならOrderBy、元のListを直接並び替えてよいならSort、と考えると分かりやすいです。

2-4. nullや空のListをSortするとどうなるか

空のListに対してSortを呼び出してもエラーにはなりません。

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

numbers.Sort();

Console.WriteLine(numbers.Count);
// 0

要素が1つだけのListでも問題ありません。

C#
var numbers = new List<int> { 10 };

numbers.Sort();

Console.WriteLine(string.Join(", ", numbers));
// 10

ただし、List変数自体がnullの場合はエラーになります。

C#
List<int>? numbers = null;

// NullReferenceException
numbers.Sort();

この場合は、Sortの前にnullチェックをします。

C#
if (numbers != null)
{
numbers.Sort();
}

また、参照型のListにnull要素が含まれている場合も注意が必要です。

C#
var names = new List<string?> { "Taro", null, "Hanako" };

names.Sort();

foreach (var name in names)
{
Console.WriteLine(name ?? "(null)");
}

標準の比較ではnullを扱える場合がありますが、自分で比較処理を書くときにx.Lengthのように直接プロパティへアクセスすると、nullで例外が発生します。

C#
var names = new List<string?> { "Taro", null, "Hanako" };

// nullがあると危険
names.Sort((a, b) => a!.Length.CompareTo(b!.Length));

nullを含む可能性がある場合は、明示的に処理しましょう。

C#
names.Sort((a, b) =>
{
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;

return a.Length.CompareTo(b.Length);
});

3. List.Sortで降順に並び替える方法

3-1. Sort後にReverseを使って降順にする方法

もっとも分かりやすい降順ソートは、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 fruits = new List<string> { "banana", "apple", "orange" };

fruits.Sort();
fruits.Reverse();

Console.WriteLine(string.Join(", ", fruits));
// orange, banana, apple

この方法は読みやすく、初心者にも分かりやすい書き方です。

ただし、並び替え条件が複雑になってくる場合は、最初から比較処理で降順を指定した方が自然です。

3-2. 比較処理を指定して最初から降順にする方法

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

降順の場合は、比較する順番を逆にします。

C#
numbers.Sort((a, b) => b.CompareTo(a));

つまり、a.CompareTo(b)なら昇順、b.CompareTo(a)なら降順です。

自作クラスのプロパティでも同じ考え方です。

C#
users.Sort((a, b) => b.Age.CompareTo(a.Age));

これは、年齢が大きい順に並び替える処理です。

3-3. Comparer<T>.Createを使った降順ソート

Comparer<T>.Createを使って、比較ルールを作ることもできます。

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

var descendingComparer = Comparer<int>.Create((a, b) => b.CompareTo(a));

numbers.Sort(descendingComparer);

Console.WriteLine(string.Join(", ", numbers));
// 8, 5, 3, 2, 1

この書き方は、比較ルールに名前を付けたい場合や、複数箇所で同じ比較ルールを使いたい場合に便利です。

自作クラスでも使えます。

C#
var ageDescendingComparer = Comparer<User>.Create(
(a, b) => b.Age.CompareTo(a.Age)
);

users.Sort(ageDescendingComparer);

比較処理が短い場合はラムダ式を直接書く、再利用したい場合はComparer<T>.Createを使う、と考えるとよいでしょう。

3-4. 降順ソートでよくある書き方の違い

降順ソートには、いくつかの書き方があります。

C#
numbers.Sort();
numbers.Reverse();

これは、昇順に並び替えてから逆順にする書き方です。シンプルで分かりやすいです。

C#
numbers.Sort((a, b) => b.CompareTo(a));

これは、比較処理で直接降順を指定する書き方です。自作クラスやプロパティ指定のソートでよく使います。

C#
var sorted = numbers.OrderByDescending(x => x).ToList();

これはLINQを使う方法です。元のListを変更せずに、降順の新しいListを作ります。

それぞれの違いを整理すると、次のようになります。

C#
// 元のListを変更してよい。簡単に降順にしたい。
numbers.Sort();
numbers.Reverse();

// 元のListを変更してよい。条件を指定して降順にしたい。
numbers.Sort((a, b) => b.CompareTo(a));

// 元のListを残したい。
var sorted = numbers.OrderByDescending(x => x).ToList();

4. ラムダ式を使ってList.Sortの並び替え条件を指定する

4-1. Comparison<T>を使ったSortの書き方

List.Sortでは、ラムダ式で比較処理を指定できます。

C#
list.Sort((a, b) => a.CompareTo(b));

このラムダ式は、Comparison<T>というデリゲートに対応しています。

Comparison<T>は、2つの値を比較して、intを返す処理です。

C#
int Compare(T a, T b)

戻り値の意味は次のとおりです。

C#
// aをbより前に並べたい場合
return -1;

// aとbを同じ順序として扱う場合
return 0;

// aをbより後ろに並べたい場合
return 1;

実際には、CompareToを使って書くことが多いです。

C#
numbers.Sort((a, b) => a.CompareTo(b));

4-2. 数値を昇順・降順にするラムダ式サンプル

数値を昇順にする場合は、次のように書きます。

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

numbers.Sort((a, b) => a.CompareTo(b));

Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3, 4, 5

降順にする場合は、abを逆にします。

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

numbers.Sort((a, b) => b.CompareTo(a));

Console.WriteLine(string.Join(", ", numbers));
// 5, 4, 3, 2, 1

次のように引き算で書く例も見かけます。

C#
numbers.Sort((a, b) => a - b);

ただし、値が大きい場合にオーバーフローする可能性があるため、基本的にはCompareToを使う方が安全です。

C#
numbers.Sort((a, b) => a.CompareTo(b));

4-3. 文字列の長さで並び替えるサンプル

文字列をアルファベット順ではなく、文字数の短い順に並べたい場合は、Lengthを比較します。

C#
var words = new List<string> { "apple", "kiwi", "banana", "fig" };

words.Sort((a, b) => a.Length.CompareTo(b.Length));

Console.WriteLine(string.Join(", ", words));
// fig, kiwi, apple, banana

長い順にしたい場合は、比較を逆にします。

C#
words.Sort((a, b) => b.Length.CompareTo(a.Length));

Console.WriteLine(string.Join(", ", words));
// banana, apple, kiwi, fig

文字数が同じ場合にアルファベット順にしたい場合は、複数条件で比較します。

C#
words.Sort((a, b) =>
{
int lengthResult = a.Length.CompareTo(b.Length);

if (lengthResult != 0)
{
return lengthResult;
}

return a.CompareTo(b);
});

この例では、まず文字数で比較し、文字数が同じ場合だけ文字列そのものを比較しています。

4-4. 複数条件で並び替えたい場合の考え方

複数条件で並び替える場合は、優先したい条件から順番に比較します。

たとえば、ユーザー一覧を「年齢の昇順、年齢が同じなら名前順」にしたい場合は、次のように書きます。

C#
var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 30 },
new User { Name = "Akira", Age = 25 }
};

users.Sort((a, b) =>
{
int ageResult = a.Age.CompareTo(b.Age);

if (ageResult != 0)
{
return ageResult;
}

return a.Name.CompareTo(b.Name);
});

foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}

出力例です。

C#
Akira: 25
Hanako: 25
Jiro: 30
Taro: 30

比較処理では、最初の条件で差があればその結果を返します。差がなければ次の条件を比較します。

LINQを使う場合は、OrderByThenByでより読みやすく書けます。

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

元のListを直接変更したいならSort、複数条件を読みやすく書きたいならOrderByThenByが便利です。

5. 自作クラスのListをSortで並び替える方法

5-1. 自作クラスをそのままSortするとエラーになる理由

自作クラスのListに対して、何も指定せずにSort()を呼び出すとエラーになることがあります。

C#
var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 }
};

// エラーになる可能性がある
users.Sort();

intstringは標準で比較方法を持っています。

しかし、自作クラスの場合、C#は「何を基準に並び替えればよいか」を判断できません。

たとえば、次のUserクラスがあるとします。

C#
class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}

このクラスにはNameAgeがあります。

名前順にしたいのか、年齢順にしたいのか、C#には分かりません。そのため、比較ルールを指定する必要があります。

5-2. プロパティを指定して並び替えるサンプル

自作クラスのListを並び替えるには、ラムダ式で比較するプロパティを指定します。

C#
var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};

users.Sort((a, b) => a.Age.CompareTo(b.Age));

foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}

出力結果です。

C#
Jiro: 20
Hanako: 25
Taro: 30

a.Age.CompareTo(b.Age)と書くことで、Ageの昇順に並び替えています。

名前順にしたい場合は、Nameを比較します。

C#
users.Sort((a, b) => a.Name.CompareTo(b.Name));

このように、自作クラスでは「どのプロパティで比較するか」を明示するのが基本です。

5-3. 年齢・点数・日付など数値系プロパティで並び替える

年齢順に並び替える例です。

C#
users.Sort((a, b) => a.Age.CompareTo(b.Age));

年齢の降順にする場合は、次のようにします。

C#
users.Sort((a, b) => b.Age.CompareTo(a.Age));

点数順に並び替える例です。

C#
class Student
{
public string Name { get; set; } = "";
public int Score { get; set; }
}
C#
var students = new List<Student>
{
new Student { Name = "Taro", Score = 80 },
new Student { Name = "Hanako", Score = 95 },
new Student { Name = "Jiro", Score = 70 }
};

students.Sort((a, b) => a.Score.CompareTo(b.Score));

foreach (var student in students)
{
Console.WriteLine($"{student.Name}: {student.Score}");
}

点数が高い順にしたい場合は、降順にします。

C#
students.Sort((a, b) => b.Score.CompareTo(a.Score));

日付で並び替える場合も同じです。

C#
class Article
{
public string Title { get; set; } = "";
public DateTime PublishedAt { get; set; }
}
C#
var articles = new List<Article>
{
new Article { Title = "記事A", PublishedAt = new DateTime(2024, 5, 1) },
new Article { Title = "記事B", PublishedAt = new DateTime(2024, 3, 10) },
new Article { Title = "記事C", PublishedAt = new DateTime(2024, 6, 20) }
};

articles.Sort((a, b) => a.PublishedAt.CompareTo(b.PublishedAt));

新しい順にしたい場合は、次のようにします。

C#
articles.Sort((a, b) => b.PublishedAt.CompareTo(a.PublishedAt));

5-4. 名前など文字列プロパティで並び替える

名前順に並び替える場合は、文字列プロパティを比較します。

C#
users.Sort((a, b) => a.Name.CompareTo(b.Name));

サンプル全体は次のとおりです。

C#
var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};

users.Sort((a, b) => a.Name.CompareTo(b.Name));

foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}

出力例です。

C#
Hanako: 25
Jiro: 20
Taro: 30

大文字・小文字を無視して並び替えたい場合は、StringComparerを使います。

C#
users.Sort((a, b) =>
StringComparer.OrdinalIgnoreCase.Compare(a.Name, b.Name)
);

Nameがnullになる可能性がある場合は、null対策も入れておくと安全です。

C#
users.Sort((a, b) =>
string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)
);

5-5. 昇順・降順を切り替える書き方

昇順と降順は、CompareToの左右を入れ替えることで切り替えられます。

年齢の昇順です。

C#
users.Sort((a, b) => a.Age.CompareTo(b.Age));

年齢の降順です。

C#
users.Sort((a, b) => b.Age.CompareTo(a.Age));

名前の昇順です。

C#
users.Sort((a, b) => a.Name.CompareTo(b.Name));

名前の降順です。

C#
users.Sort((a, b) => b.Name.CompareTo(a.Name));

フラグで昇順・降順を切り替えたい場合は、次のように書けます。

C#
bool ascending = false;

users.Sort((a, b) =>
{
int result = a.Age.CompareTo(b.Age);
return ascending ? result : -result;
});

ただし、単純な比較であれば、次のように分けて書いた方が読みやすい場合もあります。

C#
if (ascending)
{
users.Sort((a, b) => a.Age.CompareTo(b.Age));
}
else
{
users.Sort((a, b) => b.Age.CompareTo(a.Age));
}

6. IComparableとIComparerを使った本格的な並び替え

6-1. IComparable<T>でクラス自身に並び替えルールを持たせる

IComparable<T>を実装すると、自作クラス自身に標準の並び替えルールを持たせることができます。

C#
class User : IComparable<User>
{
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);
}
}

このようにしておくと、users.Sort()だけで年齢順に並び替えられます。

C#
var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 20 }
};

users.Sort();

foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}");
}

出力結果です。

C#
Jiro: 20
Hanako: 25
Taro: 30

IComparable<T>は、そのクラスの自然な並び順を定義したい場合に向いています。

たとえば、Userは年齢順で並ぶのが基本、Productは価格順で並ぶのが基本、というように決めたい場合です。

6-2. IComparer<T>で外部に並び替えルールを分離する

IComparer<T>を使うと、並び替えルールをクラスの外に分離できます。

C#
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);
}
}

使うときは、Sortに比較クラスのインスタンスを渡します。

C#
users.Sort(new UserAgeComparer());

名前順のComparerも作れます。

C#
class UserNameComparer : 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 string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
}
C#
users.Sort(new UserNameComparer());

IComparer<T>を使うと、同じクラスに対して複数の並び替えルールを用意できます。

6-3. IComparableとIComparerの使い分け

IComparable<T>は、クラス自身に基本の並び順を持たせる方法です。

C#
class Product : IComparable<Product>
{
public string Name { get; set; } = "";
public int Price { get; set; }

public int CompareTo(Product? other)
{
if (other == null) return 1;

return Price.CompareTo(other.Price);
}
}

この場合、Productは価格順で並ぶのが標準ルールになります。

C#
products.Sort();

一方、IComparer<T>は、外部に並び替えルールを作る方法です。

C#
class ProductNameComparer : IComparer<Product>
{
public int Compare(Product? x, Product? y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;

return x.Name.CompareTo(y.Name);
}
}
C#
products.Sort(new ProductNameComparer());

使い分けの目安は次のとおりです。

C#
// クラスの標準的な並び順を決めたい
IComparable<T>

// 場面ごとに並び替えルールを変えたい
IComparer<T>

実務では、単発の並び替えならラムダ式、再利用したいならIComparer<T>、クラスの基本ルールとして定義したいならIComparable<T>を使うと分かりやすいです。

6-4. Compareメソッドの戻り値の意味

CompareCompareToでは、戻り値の意味を理解しておくことが重要です。

C#
public int Compare(User? x, User? y)
{
return x.Age.CompareTo(y.Age);
}

戻り値が負の数なら、xyより前に並びます。

戻り値が0なら、xyは同じ順序として扱われます。

戻り値が正の数なら、xyより後ろに並びます。

たとえば、年齢の昇順は次のように書きます。

C#
return x.Age.CompareTo(y.Age);

年齢の降順は次のように書きます。

C#
return y.Age.CompareTo(x.Age);

または、結果を反転させることもできます。

C#
return -x.Age.CompareTo(y.Age);

ただし、可読性を考えると、次のように書く方が分かりやすいことが多いです。

C#
return y.Age.CompareTo(x.Age);

7. List.SortとLINQのOrderBy・OrderByDescendingの違い

7-1. List.Sortは元のListを変更する

List.Sortは、元のListを直接変更します。

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

numbers.Sort();

Console.WriteLine(string.Join(", ", numbers));
// 1, 2, 3

並び替え後は、元の順番は残りません。

C#
var users = new List<User>
{
new User { Name = "Taro", Age = 30 },
new User { Name = "Hanako", Age = 25 }
};

users.Sort((a, b) => a.Age.CompareTo(b.Age));

このコードを実行すると、users自体の順番が年齢順に変わります。

元のListをそのまま使い続けたい場合は、Sortする前にコピーを作る必要があります。

C#
var sortedUsers = new List<User>(users);
sortedUsers.Sort((a, b) => a.Age.CompareTo(b.Age));

7-2. OrderByは新しい並び順の結果を返す

LINQのOrderByは、元のListを変更せず、並び替えた結果を返します。

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

var sortedNumbers = numbers.OrderBy(x => x).ToList();

Console.WriteLine(string.Join(", ", numbers));
// 3, 1, 2

Console.WriteLine(string.Join(", ", sortedNumbers));
// 1, 2, 3

降順にする場合は、OrderByDescendingを使います。

C#
var sortedNumbers = numbers.OrderByDescending(x => x).ToList();

Console.WriteLine(string.Join(", ", sortedNumbers));
// 3, 2, 1

自作クラスでも、プロパティを指定して簡単に並び替えられます。

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

降順なら次のようにします。

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

7-3. 単純な並び替えならどちらを使うべきか

元のListを変更してよいなら、List.Sortで問題ありません。

C#
numbers.Sort();

元のListを変更したくないなら、OrderByを使います。

C#
var sortedNumbers = numbers.OrderBy(x => x).ToList();

単純な数値や文字列の並び替えなら、どちらでも実現できます。

ただし、コードの意図は少し違います。

C#
// このListそのものを並び替える
numbers.Sort();

// 並び替えた別の結果を作る
var sorted = numbers.OrderBy(x => x).ToList();

実務では、画面表示用に一時的に並び替えたい場合はOrderBy、内部データの順番そのものを変えたい場合はSortが使いやすいです。

7-4. ThenByを使った複数条件ソートとの比較

複数条件の並び替えは、LINQのThenByを使うと読みやすく書けます。

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

これは、「年齢の昇順、年齢が同じなら名前順」という意味です。

降順を組み合わせることもできます。

C#
var sortedUsers = users
.OrderByDescending(user => user.Age)
.ThenBy(user => user.Name)
.ToList();

同じことをList.Sortで書くと、次のようになります。

C#
users.Sort((a, b) =>
{
int ageResult = b.Age.CompareTo(a.Age);

if (ageResult != 0)
{
return ageResult;
}

return a.Name.CompareTo(b.Name);
});

List.Sortでも複数条件ソートは可能ですが、条件が増えるとコードが長くなります。

読みやすさを重視するなら、複数条件ではOrderByThenByを使うのも有力です。

8. List.Sortでよくあるエラーと注意点

8-1. Failed to compare two elements in the arrayの原因

List.Sortを使っていると、次のようなエラーが出ることがあります。

C#
Failed to compare two elements in the array.

これは、並び替え中に要素同士を比較できなかった場合に発生します。

よくある原因は次のようなものです。

C#
// 自作クラスに比較ルールがない
users.Sort();

// 比較処理の中で例外が発生している
users.Sort((a, b) => a.Name.Length.CompareTo(b.Name.Length));

// nullを考慮していない
users.Sort((a, b) => a.Name.CompareTo(b.Name));

たとえば、Nameがnullの可能性があるのに、直接CompareToを呼び出すと例外が発生します。

C#
users.Sort((a, b) => a.Name.CompareTo(b.Name));

安全に書くなら、string.Compareを使う方法があります。

C#
users.Sort((a, b) =>
string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)
);

8-2. 自作クラスで比較ルールを指定していない場合

自作クラスをそのままSort()しようとすると、比較方法が分からずエラーになることがあります。

C#
var products = new List<Product>
{
new Product { Name = "PC", Price = 120000 },
new Product { Name = "Mouse", Price = 3000 }
};

// 比較ルールがないためエラーになる可能性がある
products.Sort();

この場合は、プロパティを指定して並び替えます。

C#
products.Sort((a, b) => a.Price.CompareTo(b.Price));

または、IComparable<T>を実装します。

C#
class Product : IComparable<Product>
{
public string Name { get; set; } = "";
public int Price { get; set; }

public int CompareTo(Product? other)
{
if (other == null) return 1;

return Price.CompareTo(other.Price);
}
}

これで、次のように書けるようになります。

C#
products.Sort();

8-3. nullを含むListを並び替えるときの注意

Listの中にnullが含まれている場合、比較処理でエラーになることがあります。

C#
var users = new List<User?>
{
new User { Name = "Taro", Age = 30 },
null,
new User { Name = "Hanako", Age = 25 }
};

この状態で、次のように書くと危険です。

C#
users.Sort((a, b) => a!.Age.CompareTo(b!.Age));

aまたはbがnullの場合、例外が発生します。

nullを含む可能性がある場合は、明示的に処理します。

C#
users.Sort((a, b) =>
{
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;

return a.Age.CompareTo(b.Age);
});

この例では、nullを先頭に並べています。

nullを最後にしたい場合は、戻り値を逆にします。

C#
users.Sort((a, b) =>
{
if (a == null && b == null) return 0;
if (a == null) return 1;
if (b == null) return -1;

return a.Age.CompareTo(b.Age);
});

8-4. CompareToで戻り値を逆にしてしまうミス

CompareToを使うときによくあるミスが、昇順と降順を逆に書いてしまうことです。

昇順は次の書き方です。

C#
numbers.Sort((a, b) => a.CompareTo(b));

降順は次の書き方です。

C#
numbers.Sort((a, b) => b.CompareTo(a));

自作クラスでも同じです。

C#
// 年齢の昇順
users.Sort((a, b) => a.Age.CompareTo(b.Age));

// 年齢の降順
users.Sort((a, b) => b.Age.CompareTo(a.Age));

「小さい順にしたいのに大きい順になった」「ランキングの順位が逆になった」という場合は、CompareToの左右が逆になっていないか確認しましょう。

8-5. Sortは安定ソートではない点に注意

List.Sortは安定ソートではありません。

安定ソートとは、比較結果が同じ要素同士の元の順番が維持されるソートのことです。

たとえば、年齢だけで並び替える場合を考えます。

C#
var users = new List<User>
{
new User { Name = "A", Age = 20 },
new User { Name = "B", Age = 20 },
new User { Name = "C", Age = 30 }
};

users.Sort((a, b) => a.Age.CompareTo(b.Age));

ABはどちらもAgeが20です。

安定ソートなら、元の順番であるABが維持されます。しかし、List.Sortでは同じキーの要素の順序が保証されません。

同じ値のときにも順番を決めたい場合は、複数条件を指定します。

C#
users.Sort((a, b) =>
{
int ageResult = a.Age.CompareTo(b.Age);

if (ageResult != 0)
{
return ageResult;
}

return a.Name.CompareTo(b.Name);
});

元の順番を維持した安定ソートが必要な場合は、LINQのOrderByを使うことも検討しましょう。

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

9. 実践サンプルで学ぶList.Sortの使い方

9-1. 商品価格を安い順・高い順に並び替える

商品クラスを用意します。

C#
class Product
{
public string Name { get; set; } = "";
public int Price { get; set; }
}

商品リストを作成します。

C#
var products = new List<Product>
{
new Product { Name = "ノートPC", Price = 120000 },
new Product { Name = "マウス", Price = 3000 },
new Product { Name = "キーボード", Price = 8000 },
new Product { Name = "モニター", Price = 25000 }
};

安い順に並び替える場合です。

C#
products.Sort((a, b) => a.Price.CompareTo(b.Price));

foreach (var product in products)
{
Console.WriteLine($"{product.Name}: {product.Price}円");
}

出力例です。

C#
マウス: 3000
キーボード: 8000
モニター: 25000
ノートPC: 120000

高い順に並び替える場合です。

C#
products.Sort((a, b) => b.Price.CompareTo(a.Price));

出力例です。

C#
ノートPC: 120000
モニター: 25000
キーボード: 8000
マウス: 3000

価格順の並び替えは、ECサイトの商品一覧や管理画面などでよく使われます。

9-2. ユーザー一覧を年齢順に並び替える

ユーザー一覧を年齢順に並び替える例です。

C#
class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
C#
var users = new List<User>
{
new User { Name = "Taro", Age = 32 },
new User { Name = "Hanako", Age = 25 },
new User { Name = "Jiro", Age = 28 }
};

年齢の若い順に並び替えます。

C#
users.Sort((a, b) => a.Age.CompareTo(b.Age));

foreach (var user in users)
{
Console.WriteLine($"{user.Name}: {user.Age}歳");
}

出力例です。

C#
Hanako: 25
Jiro: 28
Taro: 32

年齢の高い順にする場合です。

C#
users.Sort((a, b) => b.Age.CompareTo(a.Age));

年齢が同じ場合に名前順にしたいなら、複数条件を指定します。

C#
users.Sort((a, b) =>
{
int ageResult = a.Age.CompareTo(b.Age);

if (ageResult != 0)
{
return ageResult;
}

return a.Name.CompareTo(b.Name);
});

9-3. 日付の新しい順・古い順に並び替える

記事やニュース、ログなどでは、日付順の並び替えをよく使います。

C#
class News
{
public string Title { get; set; } = "";
public DateTime PublishedAt { get; set; }
}
C#
var newsList = new List<News>
{
new News { Title = "ニュースA", PublishedAt = new DateTime(2024, 4, 10) },
new News { Title = "ニュースB", PublishedAt = new DateTime(2024, 6, 1) },
new News { Title = "ニュースC", PublishedAt = new DateTime(2024, 5, 20) }
};

古い順に並び替える場合です。

C#
newsList.Sort((a, b) => a.PublishedAt.CompareTo(b.PublishedAt));

foreach (var news in newsList)
{
Console.WriteLine($"{news.PublishedAt:yyyy/MM/dd} {news.Title}");
}

出力例です。

C#
2024/04/10 ニュースA
2024/05/20 ニュースC
2024/06/01 ニュースB

新しい順に並び替える場合です。

C#
newsList.Sort((a, b) => b.PublishedAt.CompareTo(a.PublishedAt));

出力例です。

C#
2024/06/01 ニュースB
2024/05/20 ニュースC
2024/04/10 ニュースA

新着順の一覧を作る場合は、日付の降順を使うことが多いです。

9-4. スコアが高い順にランキング表示する

最後に、ランキング表示の例です。

C#
class Player
{
public string Name { get; set; } = "";
public int Score { get; set; }
}
C#
var players = new List<Player>
{
new Player { Name = "Taro", Score = 1200 },
new Player { Name = "Hanako", Score = 1800 },
new Player { Name = "Jiro", Score = 1500 },
new Player { Name = "Akira", Score = 1800 }
};

スコアが高い順に並び替えます。

C#
players.Sort((a, b) => b.Score.CompareTo(a.Score));

int rank = 1;

foreach (var player in players)
{
Console.WriteLine($"{rank}位 {player.Name}: {player.Score}点");
rank++;
}

出力例です。

C#
1 Hanako: 1800
2 Akira: 1800
3 Jiro: 1500
4 Taro: 1200

スコアが同じ場合に名前順にしたい場合は、複数条件を指定します。

C#
players.Sort((a, b) =>
{
int scoreResult = b.Score.CompareTo(a.Score);

if (scoreResult != 0)
{
return scoreResult;
}

return a.Name.CompareTo(b.Name);
});

このようにすると、「スコアが高い順、同点なら名前順」というランキングを作れます。

C#
1 Akira: 1800
2 Hanako: 1800
3 Jiro: 1500
4 Taro: 1200

まとめ

C#のList.Sortは、List<T>の要素を並び替えるための基本的なメソッドです。

数値や文字列のように標準で比較できる型であれば、Sort()を呼び出すだけで昇順に並び替えられます。

C#
numbers.Sort();

降順にしたい場合は、Sort後にReverseする方法があります。

C#
numbers.Sort();
numbers.Reverse();

また、ラムダ式を使えば、最初から降順や特定のプロパティ順に並び替えできます。

C#
numbers.Sort((a, b) => b.CompareTo(a));

自作クラスのListを並び替える場合は、どのプロパティで比較するのかを指定する必要があります。

C#
users.Sort((a, b) => a.Age.CompareTo(b.Age));

本格的に並び替えルールを管理したい場合は、IComparable<T>IComparer<T>を使う方法もあります。

C#
products.Sort(new ProductPriceComparer());

List.SortとLINQのOrderByの違いも重要です。

List.Sortは元のListを変更します。

C#
numbers.Sort();

OrderByは元のListを変更せず、並び替えた結果を返します。

C#
var sortedNumbers = numbers.OrderBy(x => x).ToList();

元のListを直接並び替えたい場合はList.Sort、元のListを残したい場合はOrderByを使うとよいでしょう。

List.Sortを使いこなせるようになると、数値、文字列、日付、自作クラス、ランキング、商品一覧など、さまざまなデータを目的に合わせて並び替えられるようになります。