0Pricing
C# Academy · 课时

使用 IComparer 自定义排序

使用比较器提供其他排序方式。

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

以不同方式排序

一种类型通过 IComparable<T> 只能有一个自然顺序。若要以其他方式排序同一份数据,请在调用位置提供 IComparer<T> 或 Comparison<T> 委托。

实现比较器

IComparer<T> 是一个独立对象,包含 Compare(x, y) 方法。它遵循与 CompareTo 相同的负数、零、正数约定。

using System;
using System.Collections.Generic;

public class Person
{
    public string Name;
    public int Age;
    public Person(string name, int age) { Name = name; Age = age; }
    public override string ToString() => Name + "(" + Age + ")";
}

public class ByName : IComparer<Person>
{
    public int Compare(Person x, Person y)
        => string.Compare(x.Name, y.Name, StringComparison.Ordinal);
}

public class Program
{
    public static void Main()
    {
        var people = new List<Person> { new Person("Zoe", 1), new Person("Ann", 2) };
        people.Sort(new ByName());
        Console.WriteLine(string.Join(", ", people));
    }
}

为一种类型定义多个比较器

您可以定义多个比较器,并在每次排序时选择其中一个。这里可以根据需要按姓名或年龄对同一组 people 进行排序。

using System;
using System.Collections.Generic;

public class Person
{
    public string Name;
    public int Age;
    public Person(string name, int age) { Name = name; Age = age; }
    public override string ToString() => Name + "(" + Age + ")";
}

public class ByAge : IComparer<Person>
{
    public int Compare(Person x, Person y) => x.Age.CompareTo(y.Age);
}

public class Program
{
    public static void Main()
    {
        var people = new List<Person> { new Person("Ann", 40), new Person("Bo", 20) };
        people.Sort(new ByAge());
        Console.WriteLine(string.Join(", ", people));
    }
}

比较委托

对于一次性排序,Comparison<T> 委托(通常是匿名函数)比完整的类更加简洁。List.Sort 可以直接接受它。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var words = new List<string> { "banana", "fig", "apple" };
        // Sort by length using a Comparison<string> lambda
        words.Sort((a, b) => a.Length.CompareTo(b.Length));
        Console.WriteLine(string.Join(", ", words));
    }
}

使用比较器进行降序排序

在 Compare 中交换操作数即可反转顺序。这样无需修改类型本身,就能实现降序排序。

using System;
using System.Collections.Generic;

public class DescendingInt : IComparer<int>
{
    public int Compare(int x, int y) => y.CompareTo(x);
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 3, 1, 4, 1, 5 };
        nums.Sort(new DescendingInt());
        Console.WriteLine(string.Join(", ", nums));
    }
}

OrderBy 中的比较器

LINQ 的 OrderBy 接受 IComparer<TKey> 作为第二个参数,因此您可以自定义所选键的比较方式。

using System;
using System.Collections.Generic;
using System.Linq;

public class CaseInsensitive : IComparer<string>
{
    public int Compare(string x, string y)
        => string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}

public class Program
{
    public static void Main()
    {
        var names = new[] { "bob", "Alice", "carol" };
        foreach (var n in names.OrderBy(x => x, new CaseInsensitive()))
            Console.WriteLine(n);
    }
}

多键比较

比较器可以按优先级顺序比较多个键。先计算第一个键;如果结果相等,再继续比较下一个键。

using System;
using System.Collections.Generic;

public class Employee
{
    public string Dept;
    public int Salary;
    public Employee(string dept, int salary) { Dept = dept; Salary = salary; }
    public override string ToString() => Dept + ":" + Salary;
}

public class ByDeptThenSalary : IComparer<Employee>
{
    public int Compare(Employee x, Employee y)
    {
        int byDept = string.Compare(x.Dept, y.Dept, StringComparison.Ordinal);
        return byDept != 0 ? byDept : x.Salary.CompareTo(y.Salary);
    }
}

public class Program
{
    public static void Main()
    {
        var staff = new List<Employee>
        {
            new Employee("IT", 50), new Employee("HR", 40), new Employee("IT", 30)
        };
        staff.Sort(new ByDeptThenSalary());
        Console.WriteLine(string.Join(", ", staff));
    }
}

Comparer.Create 快捷方式

Comparer<T>.Create 可以根据匿名函数创建 IComparer<T>,兼具委托的简洁性和需要比较器的接口所提供的功能。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var byLengthDesc = Comparer<string>.Create((a, b) => b.Length.CompareTo(a.Length));
        var words = new List<string> { "hi", "hello", "hey" };
        words.Sort(byLengthDesc);
        Console.WriteLine(string.Join(", ", words));
    }
}

在多个集合中复用比较器

同一个比较器实例可以用于排序、搜索和有序集合。只定义一次,就能让它在所有使用位置都遵循一致的排序规则。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        IComparer<int> desc = Comparer<int>.Create((a, b) => b.CompareTo(a));
        var set = new SortedSet<int>(desc) { 1, 5, 3 };
        Console.WriteLine(string.Join(", ", set));
    }
}

选择自然比较还是自定义比较

如果类型只有一种内置的自然顺序,请使用 IComparable<T>。如果需要在调用位置决定多种备用的、特定上下文的顺序,请使用 IComparer<T> 或 Comparison<T>。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 5, 2, 8, 1 };
        nums.Sort(); // natural ascending (int is IComparable)
        Console.WriteLine(string.Join(", ", nums));
        nums.Sort((a, b) => b - a); // custom descending via delegate
        Console.WriteLine(string.Join(", ", nums));
    }
}

动手实践

使用比较器和匿名函数,以三种不同方式对同一个列表进行排序,全程无需修改元素类型。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var words = new List<string> { "pear", "fig", "apple", "kiwi" };

        words.Sort(); // natural alphabetical
        Console.WriteLine(string.Join(", ", words));

        words.Sort((a, b) => a.Length.CompareTo(b.Length)); // by length
        Console.WriteLine(string.Join(", ", words));

        words.Sort(Comparer<string>.Create((a, b) => b.CompareTo(a))); // reverse alphabetical
        Console.WriteLine(string.Join(", ", words));
    }
}

快速检查

选择正确的排序抽象。

回顾

自定义排序使用调用位置提供的比较器。

  • IComparer<T> 使用负数、零、正数约定实现 Compare(x, y)。
  • Comparison<T> 委托和匿名函数适合一次性排序。
  • Comparer<T>.Create 可以将匿名函数连接到该接口。
  • 使用 IComparable 表示自然顺序,使用比较器表示其他排序方式。

常见问题解答

「使用 IComparer 自定义排序」课时是免费的吗?

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

「使用 IComparer 自定义排序」这节课中我会学到什么?

使用比较器提供其他排序方式。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「使用 IComparer 自定义排序」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 实现 IEquatable
  2. 重写 GetHashCode
  3. 实现 IComparable
  4. 使用 IComparer 自定义排序
← 返回 C# Academy