0Pricing
C# Academy · 课时

实现 IComparable

使用 CompareTo 定义自然排序顺序。

实现 IComparable 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

使用 IComparable<T> 进行排序

IComparable<T> 通过一个方法 CompareTo 为类型定义自然顺序。实现后,您的类型就可以通过 Array.Sort、List.Sort 和有序集合进行排序。

CompareTo 规则

如果当前实例小于另一个实例,CompareTo 返回负数;如果相等,返回零;如果大于另一个实例,返回正数。直接比较底层值通常就可以实现这一规则。

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

按主要字段比较

对于类,请选择定义其自然顺序的字段。这里按年龄对 people 进行排序,并委托给 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));
    }
}

按多个字段打破平局

当主要键相同时,请比较次要字段。先计算第一次比较的结果,只有结果为零时,才继续使用下一个字段。

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 的操作数来反转比较结果。这是一种无需单独比较器即可按从高到低排序的简洁方式。

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 返回零,则这些项目在排序意义上被视为相等。请让它与 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> 后,领域概念就能成为整个框架都可以排序的对象。这里,tasks 会先按优先级、再按名称排序。

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 定义自然顺序。

  • 负数、零、正数分别表示小于、相等、大于。
  • 支持 List.Sort、Array.Sort 和有序集合。
  • 出现相等时,通过比较次要字段来确定顺序。
  • 保持 CompareTo == 0 与 Equals 的一致性。

常见问题解答

「实现 IComparable」课时是免费的吗?

是的 — 「实现 IComparable」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「实现 IComparable」这节课中我会学到什么?

使用 CompareTo 定义自然排序顺序。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 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