IComparableの実装
CompareToで自然な並べ替え順を定義します。
「IComparableの実装」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。
IComparable<T> による順序付け
IComparable<T> は、1つのメソッド CompareTo を通じて型の自然な順序を定義します。実装すると、Array.Sort、List.Sort、順序付きコレクションで自分の型を並べ替えられます。
CompareTo の契約
CompareTo は、このインスタンスが相手より小さい場合は負の数、等しい場合は0、大きい場合は正の数を返します。基になる値を比較することで、そのまま実装できることがよくあります。
using System;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Weight(100).CompareTo(new Weight(200)));
Console.WriteLine(new Weight(200).CompareTo(new Weight(200)));
Console.WriteLine(new Weight(300).CompareTo(new Weight(200)));
}
}リストを並べ替える
型が比較可能になると、List<T>.Sort() は追加の引数なしで CompareTo を使って並べ替えます。
using System;
using System.Collections.Generic;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
var list = new List<Weight> { new Weight(300), new Weight(100), new Weight(200) };
list.Sort();
Console.WriteLine(string.Join(", ", list));
}
}主要フィールドで比較する
クラスでは、自然な順序を決めるフィールドを選びます。ここでは int.CompareTo に委譲し、人を年齢順に並べます。
using System;
using System.Collections.Generic;
public class Person : IComparable<Person>
{
public string Name;
public int Age;
public Person(string name, int age) { Name = name; Age = age; }
public int CompareTo(Person other) => Age.CompareTo(other.Age);
public override string ToString() => Name + "(" + Age + ")";
}
public class Program
{
public static void Main()
{
var people = new List<Person> { new Person("Ann", 30), new Person("Bo", 20) };
people.Sort();
Console.WriteLine(string.Join(", ", people));
}
}複数フィールドで同順位を解消する
主キーが同じ場合は、2番目のフィールドを比較します。最初の比較を行い、その結果が0の場合にだけ次のフィールドへ移ります。
using System;
using System.Collections.Generic;
public class Person : IComparable<Person>
{
public string Name;
public int Age;
public Person(string name, int age) { Name = name; Age = age; }
public int CompareTo(Person other)
{
int byAge = Age.CompareTo(other.Age);
return byAge != 0 ? byAge : string.Compare(Name, other.Name, StringComparison.Ordinal);
}
public override string ToString() => Name + "(" + Age + ")";
}
public class Program
{
public static void Main()
{
var people = new List<Person> { new Person("Zoe", 30), new Person("Ann", 30) };
people.Sort();
Console.WriteLine(string.Join(", ", people));
}
}配列を並べ替える
Array.Sort も IComparable<T> に依存します。同じ比較ロジックが、配列、リスト、順序付き構造のすべてで利用されます。
using System;
public struct Score : IComparable<Score>
{
public int Points;
public Score(int p) { Points = p; }
public int CompareTo(Score other) => Points.CompareTo(other.Points);
public override string ToString() => Points.ToString();
}
public class Program
{
public static void Main()
{
var scores = new[] { new Score(50), new Score(10), new Score(30) };
Array.Sort(scores);
Console.WriteLine(string.Join(", ", (object[])scores));
}
}降順
順序を逆にするには、CompareTo のオペランドを入れ替えて比較結果を反転させます。別の comparer を用意せずに高い順で並べ替えられる、すっきりした方法です。
using System;
using System.Collections.Generic;
public struct Score : IComparable<Score>
{
public int Points;
public Score(int p) { Points = p; }
// Reversed: higher points come first
public int CompareTo(Score other) => other.Points.CompareTo(Points);
public override string ToString() => Points.ToString();
}
public class Program
{
public static void Main()
{
var list = new List<Score> { new Score(10), new Score(50), new Score(30) };
list.Sort();
Console.WriteLine(string.Join(", ", list));
}
}比較可能性と等価性を組み合わせる
CompareTo が0を返す場合、順序付けにおいて項目は等しいとみなされます。並べ替えと等価性判定が一致するよう、Equals と一貫させてください。
using System;
public struct Version : IComparable<Version>, IEquatable<Version>
{
public int Major, Minor;
public Version(int major, int minor) { Major = major; Minor = minor; }
public int CompareTo(Version other)
{
int byMajor = Major.CompareTo(other.Major);
return byMajor != 0 ? byMajor : Minor.CompareTo(other.Minor);
}
public bool Equals(Version other) => CompareTo(other) == 0;
public override bool Equals(object obj) => obj is Version v && Equals(v);
public override int GetHashCode() => HashCode.Combine(Major, Minor);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Version(1, 2).CompareTo(new Version(1, 5)));
Console.WriteLine(new Version(2, 0).Equals(new Version(2, 0)));
}
}LINQ の OrderBy で比較を使う
その場で並べ替えなくても、LINQ の OrderBy はキーセレクターを通じて比較可能な型を利用し、遅延的に並べ替えられたビューを生成できます。
using System;
using System.Collections.Generic;
using System.Linq;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
var items = new List<Weight> { new Weight(300), new Weight(100) };
foreach (var w in items.OrderBy(x => x))
Console.WriteLine(w);
}
}並べ替え可能なドメイン型
IComparable<T> を実装すると、ドメインの概念を、フレームワーク全体で順序付けできるものにできます。ここでは、タスクを優先度、次に名前の順で並べ替えます。
using System;
using System.Collections.Generic;
public class TaskItem : IComparable<TaskItem>
{
public string Name;
public int Priority;
public TaskItem(string name, int priority) { Name = name; Priority = priority; }
public int CompareTo(TaskItem other)
{
int byPriority = Priority.CompareTo(other.Priority);
return byPriority != 0 ? byPriority : string.Compare(Name, other.Name, StringComparison.Ordinal);
}
public override string ToString() => Priority + ":" + Name;
}
public class Program
{
public static void Main()
{
var tasks = new List<TaskItem>
{
new TaskItem("deploy", 2), new TaskItem("build", 1), new TaskItem("test", 1)
};
tasks.Sort();
Console.WriteLine(string.Join(", ", tasks));
}
}自分で試す
複数のフィールドを持つ比較可能な型を作成し、その型のリストを並べ替えます。追加の引数なしで、CompareTo によって自然な順序が決まります。
using System;
using System.Collections.Generic;
public class Card : IComparable<Card>
{
public int Rank;
public string Suit;
public Card(int rank, string suit) { Rank = rank; Suit = suit; }
public int CompareTo(Card other)
{
int byRank = Rank.CompareTo(other.Rank);
return byRank != 0 ? byRank : string.Compare(Suit, other.Suit, StringComparison.Ordinal);
}
public override string ToString() => Rank + Suit;
}
public class Program
{
public static void Main()
{
var hand = new List<Card>
{
new Card(10, "H"), new Card(2, "S"), new Card(10, "C")
};
hand.Sort();
Console.WriteLine(string.Join(", ", hand));
}
}クイックチェック
CompareTo の契約を思い出してください。
まとめ
IComparable<T> は、CompareTo によって自然な順序を定義します。
- 負の値、0、正の値は、それぞれ小さい、等しい、大きいことを意味します。
List.Sort、Array.Sort、順序付きコレクションを利用できるようになります。- 同順位の場合は、二次的なフィールドを比較して判定します。
CompareTo == 0とEqualsの結果に一貫性を持たせます。
よくある質問
「IComparableの実装」レッスンは無料ですか?
はい。「IComparableの実装」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。
「IComparableの実装」で何を学びますか?
CompareToで自然な並べ替え順を定義します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「IComparableの実装」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- IEquatableの実装
- GetHashCodeのオーバーライド
- IComparableの実装
- カスタムソートのためのIComparer