0Pricing
C# Academy · Урок

IComparer для пользовательской сортировки

Задавайте альтернативные порядки сортировки с помощью компараторов.

«IComparer для пользовательской сортировки» — бесплатный урок C# Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Сортировка разными способами

У типа есть только один естественный порядок, заданный через IComparable<T>. Чтобы сортировать те же данные другими способами, передайте в месте вызова IComparer<T> или делегат Comparison<T>.

Реализация IComparer<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>

Для разовой сортировки делегат 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> из лямбда-выражения, объединяя лаконичность делегата с API интерфейса, которому требуется компаратор.

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 и IComparer

Используйте 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 для пользовательской сортировки» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «IComparer для пользовательской сортировки»?

Задавайте альтернативные порядки сортировки с помощью компараторов. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «IComparer для пользовательской сортировки»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Реализация IEquatable
  2. Переопределение GetHashCode
  3. Реализация IComparable
  4. IComparer для пользовательской сортировки
← Назад к C# Academy