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 — локальная установка не требуется.
Все уроки этого курса
- Реализация IEquatable
- Переопределение GetHashCode
- Реализация IComparable
- IComparer для пользовательской сортировки