使用 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 反馈 — 无需本地设置。
此课程中的所有课时
- 实现 IEquatable
- 重写 GetHashCode
- 实现 IComparable
- 使用 IComparer 自定义排序