0Pricing
C# Academy · レッスン

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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. IEquatableの実装
  2. GetHashCodeのオーバーライド
  3. IComparableの実装
  4. カスタムソートのためのIComparer
← C# Academyに戻る