ZetCode

C# IComparer

最后修改日期 2024 年 1 月 22 日

在本文中,我们将展示如何在 C# 中使用 IComparer 接口比较值。

与数字和拉丁字符串等简单类型不同,更复杂的类型没有内在的排序。 我们必须自己定义排序。 在 C# 中,我们可以为此任务使用 Comparison 委托、IComparerIComparable 接口或 LINQ。

C# IComparer 接口

IComparer 接口定义了一个比较方法,值类型或类实现该方法来对其实例进行排序。

此接口与 List.SortList.BinarySearch 方法一起使用。 它提供了一种自定义集合排序方式的方法。 SortedDictionarySortedList 是实现此接口的类之一。

Compare(T, T)

Compare 方法比较两个对象,并返回一个值,指示一个对象小于、等于或大于另一个对象。

该接口由我们无法控制的类型使用;换句话说,由我们没有编写的代码使用。 如果代码是我们自己开发的,我们可以改用 IComparable 接口。

使用 IComparer,我们拥有更大的灵活性; 我们可以定义多个比较器或更新现有的比较器,而无需触及类型本身。 此外,它具有更简洁的设计,因为我们将排序实现与类型分离。

C# IComparer 示例

在以下示例中,我们对员工列表进行排序。

Program.cs
var employees = new List<(string, int)> 
{
    ("John Doe", 1230),
    ("Adam Novak", 670),
    ("Robin Brown", 2300),
    ("Rowan Cruise", 990),
    ("Joe Draker", 1190),
    ("Janet Doe", 980),
    ("Lucy Smith", 980),
    ("Thomas Moore", 1400)
};

employees.Sort(new SalaryComparer());
employees.ForEach(employee => Console.WriteLine(employee));

class SalaryComparer : IComparer<(string, int)> 
{
    public int Compare((string, int) emp1, (string, int) emp2) 
    {
        return emp1.Item2.CompareTo(emp2.Item2);
    }
}

在该示例中,我们在 SalaryComparer 中提供了 Compare 的实现。 我们创建具有值类型的对象。

public int Compare((string, int) emp1, (string, int) emp2) 
{
    return emp1.Item2.CompareTo(emp2.Item2);
}

Compare 方法的实现按工资对员工进行排序。

employees.Sort(new SalaryComparer());

我们对列表进行排序。 该方法在排序时会考虑已实现的 Compare 方法。

$ dotnet run
(Adam Novak, 670)
(Janet Doe, 980)
(Lucy Smith, 980)
(Rowan Cruise, 990)
(Joe Draker, 1190)
(John Doe, 1230)
(Thomas Moore, 1400)
(Robin Brown, 2300)

C# IComparer 示例 II

在以下示例中,我们对用户数组进行排序。

Program.cs
User[] users =
[
    new ("Robin", "bookseller"),
    new ("John", "gardener"),
    new ("John", "writer"),
    new ("Janet", "teacher"),
    new ("Andrew", "driver"),
    new ("Lucy", "accountant")
];

Array.Sort(users, new OccupationComparer());

foreach (var user in users)
{
    Console.WriteLine(user);
}

record User(string Name, string Occupation);

class OccupationComparer : IComparer<User>
{
    public int Compare(User? u1, User? u2)
    {
        if (u1 == null && u2 == null) return 0;
        if (u1 == null) return -1;
        if (u2 == null) return 1;
        return u1.Occupation.CompareTo(u2.Occupation);
    }
}

该示例按升序对用户数组进行排序。 要创建对象,我们使用记录。

public int Compare(User? u1, User? u2)
{
    if (u1 == null && u2 == null) return 0;
    if (u1 == null) return -1;
    if (u2 == null) return 1;
    return u1.Occupation.CompareTo(u2.Occupation);
}

该方法按职业排序。 根据定义,非空对象大于空对象。

Array.Sort(users, new OccupationComparer());

我们将比较器传递给 Array.Sort 方法。

$ dotnet run
User { Name = Lucy, Occupation = accountant }
User { Name = Robin, Occupation = bookseller }
User { Name = Andrew, Occupation = driver }
User { Name = John, Occupation = gardener }
User { Name = Janet, Occupation = teacher }
User { Name = John, Occupation = writer }

C# StringComparer

StringComparer 是用于比较字符串的内置比较器。 它表示使用特定大小写和基于区域性或序号比较规则的字符串比较操作。

Program.cs
List<string> words =
[
    "sky", "blue", "Church", "cup", "Adam", "also", "Bratislava", "bear", "snow",
    "carpet", "water", "volcano", "smell", "forest", "Earth"
];

words.Sort(StringComparer.Ordinal);
Console.WriteLine(string.Join(", ", words));

Console.WriteLine("---------------------");

words.Sort(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(string.Join(", ", words));

使用 OrdinalOrdinalIgnoreCase 规则,将单词列表与 StringComparer 进行比较。

$ dotnet run
Adam, Bratislava, Church, Earth, also, bear, blue, carpet, cup, forest, sky, ...
---------------------
Adam, also, bear, blue, Bratislava, carpet, Church, cup, Earth, forest, sky, ...

C# LINQ 使用 IComparer 排序

LINQ 的 OrderOrderDescending 具有重载变体,这些变体将 IComparer 作为参数。

Program.cs
List<string> words = [
    "world", "War", "abbot", "Caesar", "castle", "sky", "den",
    "forest", "ocean", "water", "falcon", "owl", "rain", "Earth"
];

var sorted = words.Order(StringComparer.OrdinalIgnoreCase).ToList();
sorted.ForEach(Console.WriteLine);

Console.WriteLine("-------------------------");

var sorted2 = words.OrderDescending(StringComparer.OrdinalIgnoreCase).ToList();
sorted2.ForEach(Console.WriteLine);

在该程序中,我们使用 OrderOrderDescending 对单词列表进行排序。 我们将 StringComparer.OrdinalIgnoreCase 传递给这些方法。

$ dotnet run
world
water
War
sky
rain
owl
ocean
forest
falcon
Earth
den
castle
Caesar
abbot
-------------------------
abbot
Caesar
castle
den
Earth
falcon
forest
ocean
owl
rain
sky
War
water
world

C# IComparer 多个字段

在以下示例中,我们按两个字段进行比较。

Program.cs
List<User> users =
[
    new ("Robin", "bookseller"),
    new ("Simon", "teacher"),
    new ("Arnold", "teacher"),
    new ("John", "gardener"),
    new ("Adam", "gardener"),
    new ("Peter", "gardener"),
    new ("John", "writer"),
    new ("Janet", "teacher"),
    new ("Andrew", "driver"),
    new ("Lucy", "accountant"),
    new ("Michael", "teacher")
];

users.Sort(new OccupationNameReverseComparer());

foreach (var user in users)
{
    Console.WriteLine(user);
}

record User(string Name, string Occupation);

class OccupationNameReverseComparer : IComparer<User>
{
    public int Compare(User? u1, User? u2)
    {
        if (u1 == null && u2 == null) return 0;
        if (u1 == null) return -1;
        if (u2 == null) return 1;

        int res = u1.Occupation.CompareTo(u2.Occupation);

        if (res == 0)
        {
            res = u2.Name.CompareTo(u1.Name);
        }

        return res;
    }
}

我们有具有相同职业的用户。 在这种情况下,我们然后比较他们的姓名。

public int Compare(User? u1, User? u2)
{
    if (u1 == null && u2 == null) return 0;
    if (u1 == null) return -1;
    if (u2 == null) return 1;

    int res = u1.Occupation.CompareTo(u2.Occupation);

    if (res == 0)
    {
        res = u2.Name.CompareTo(u1.Name);
    }

    return res;
}

首先,我们按用户的 Occupation 字段比较用户。 如果它们相等,则我们比较他们的 Name 字段。 我们比较名称的方式导致降序排序。

$ dotnet run 
User { Name = Lucy, Occupation = accountant }
User { Name = Robin, Occupation = bookseller }
User { Name = Andrew, Occupation = driver }
User { Name = Peter, Occupation = gardener }
User { Name = John, Occupation = gardener }
User { Name = Adam, Occupation = gardener }
User { Name = Simon, Occupation = teacher }
User { Name = Michael, Occupation = teacher }
User { Name = Janet, Occupation = teacher }
User { Name = Arnold, Occupation = teacher }
User { Name = John, Occupation = writer }

来源

IComparer 接口

在本文中,我们使用了 IComparer 接口在 C# 中对数据进行排序。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直撰写编程文章。 迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程