Реализация IComparable
Определяйте естественный порядок сортировки с помощью CompareTo.
«Реализация IComparable» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Упорядочивание с помощью IComparable<T>
IComparable<T> определяет естественный порядок типа с помощью одного метода — CompareTo. После реализации ваш тип можно сортировать с помощью Array.Sort, List.Sort и упорядоченных коллекций.
Контракт CompareTo
CompareTo возвращает отрицательное число, если этот экземпляр меньше другого, ноль — если они равны, и положительное число — если он больше. Сравнение базовых значений часто позволяет реализовать это напрямую.
using System;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Weight(100).CompareTo(new Weight(200)));
Console.WriteLine(new Weight(200).CompareTo(new Weight(200)));
Console.WriteLine(new Weight(300).CompareTo(new Weight(200)));
}
}Сортировка списка
После того как тип становится сопоставимым, List<T>.Sort() упорядочивает его с помощью CompareTo без дополнительных аргументов.
using System;
using System.Collections.Generic;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
var list = new List<Weight> { new Weight(300), new Weight(100), new Weight(200) };
list.Sort();
Console.WriteLine(string.Join(", ", list));
}
}Сравнение по основному полю
Для класса выберите поле, определяющее его естественный порядок. Здесь элементы people упорядочиваются по возрасту с делегированием сравнения методу int.CompareTo.
using System;
using System.Collections.Generic;
public class Person : IComparable<Person>
{
public string Name;
public int Age;
public Person(string name, int age) { Name = name; Age = age; }
public int CompareTo(Person other) => Age.CompareTo(other.Age);
public override string ToString() => Name + "(" + Age + ")";
}
public class Program
{
public static void Main()
{
var people = new List<Person> { new Person("Ann", 30), new Person("Bo", 20) };
people.Sort();
Console.WriteLine(string.Join(", ", people));
}
}Разрешение совпадений по нескольким полям
Если значения первичного ключа совпадают, сравнивайте вторичное поле. Выполните первое сравнение и только при результате, равном нулю, переходите к следующему.
using System;
using System.Collections.Generic;
public class Person : IComparable<Person>
{
public string Name;
public int Age;
public Person(string name, int age) { Name = name; Age = age; }
public int CompareTo(Person other)
{
int byAge = Age.CompareTo(other.Age);
return byAge != 0 ? byAge : string.Compare(Name, other.Name, StringComparison.Ordinal);
}
public override string ToString() => Name + "(" + Age + ")";
}
public class Program
{
public static void Main()
{
var people = new List<Person> { new Person("Zoe", 30), new Person("Ann", 30) };
people.Sort();
Console.WriteLine(string.Join(", ", people));
}
}Сортировка массивов
Array.Sort также использует IComparable<T>. Одна и та же логика сравнения работает для массивов, списков и упорядоченных структур.
using System;
public struct Score : IComparable<Score>
{
public int Points;
public Score(int p) { Points = p; }
public int CompareTo(Score other) => Points.CompareTo(other.Points);
public override string ToString() => Points.ToString();
}
public class Program
{
public static void Main()
{
var scores = new[] { new Score(50), new Score(10), new Score(30) };
Array.Sort(scores);
Console.WriteLine(string.Join(", ", (object[])scores));
}
}Сортировка по убыванию
Чтобы изменить порядок, инвертируйте сравнение, поменяв местами операнды CompareTo. Это удобный способ сортировать от большего к меньшему без отдельного средства сравнения.
using System;
using System.Collections.Generic;
public struct Score : IComparable<Score>
{
public int Points;
public Score(int p) { Points = p; }
// Reversed: higher points come first
public int CompareTo(Score other) => other.Points.CompareTo(Points);
public override string ToString() => Points.ToString();
}
public class Program
{
public static void Main()
{
var list = new List<Score> { new Score(10), new Score(50), new Score(30) };
list.Sort();
Console.WriteLine(string.Join(", ", list));
}
}Сопоставимость и равенство вместе
Если CompareTo возвращает ноль, элементы считаются равными с точки зрения порядка. Согласуйте это поведение с Equals, чтобы сортировка и проверка равенства не противоречили друг другу.
using System;
public struct Version : IComparable<Version>, IEquatable<Version>
{
public int Major, Minor;
public Version(int major, int minor) { Major = major; Minor = minor; }
public int CompareTo(Version other)
{
int byMajor = Major.CompareTo(other.Major);
return byMajor != 0 ? byMajor : Minor.CompareTo(other.Minor);
}
public bool Equals(Version other) => CompareTo(other) == 0;
public override bool Equals(object obj) => obj is Version v && Equals(v);
public override int GetHashCode() => HashCode.Combine(Major, Minor);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Version(1, 2).CompareTo(new Version(1, 5)));
Console.WriteLine(new Version(2, 0).Equals(new Version(2, 0)));
}
}Использование сравнения в LINQ OrderBy
Даже без сортировки на месте LINQ OrderBy может использовать ваш сопоставимый тип через селектор ключа, лениво создавая отсортированное представление.
using System;
using System.Collections.Generic;
using System.Linq;
public struct Weight : IComparable<Weight>
{
public int Grams;
public Weight(int g) { Grams = g; }
public int CompareTo(Weight other) => Grams.CompareTo(other.Grams);
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
var items = new List<Weight> { new Weight(300), new Weight(100) };
foreach (var w in items.OrderBy(x => x))
Console.WriteLine(w);
}
}Сортируемый тип предметной области
Реализация IComparable<T> превращает понятие предметной области в нечто, что весь фреймворк может упорядочивать. Здесь tasks сортируются сначала по приоритету, затем по имени.
using System;
using System.Collections.Generic;
public class TaskItem : IComparable<TaskItem>
{
public string Name;
public int Priority;
public TaskItem(string name, int priority) { Name = name; Priority = priority; }
public int CompareTo(TaskItem other)
{
int byPriority = Priority.CompareTo(other.Priority);
return byPriority != 0 ? byPriority : string.Compare(Name, other.Name, StringComparison.Ordinal);
}
public override string ToString() => Priority + ":" + Name;
}
public class Program
{
public static void Main()
{
var tasks = new List<TaskItem>
{
new TaskItem("deploy", 2), new TaskItem("build", 1), new TaskItem("test", 1)
};
tasks.Sort();
Console.WriteLine(string.Join(", ", tasks));
}
}Попробуйте сами
Создайте сопоставимый тип с несколькими полями и отсортируйте список таких объектов. Естественный порядок определяется через CompareTo без дополнительных аргументов.
using System;
using System.Collections.Generic;
public class Card : IComparable<Card>
{
public int Rank;
public string Suit;
public Card(int rank, string suit) { Rank = rank; Suit = suit; }
public int CompareTo(Card other)
{
int byRank = Rank.CompareTo(other.Rank);
return byRank != 0 ? byRank : string.Compare(Suit, other.Suit, StringComparison.Ordinal);
}
public override string ToString() => Rank + Suit;
}
public class Program
{
public static void Main()
{
var hand = new List<Card>
{
new Card(10, "H"), new Card(2, "S"), new Card(10, "C")
};
hand.Sort();
Console.WriteLine(string.Join(", ", hand));
}
}Быстрая проверка
Вспомните контракт CompareTo.
Повторение
IComparable<T> задаёт естественный порядок через CompareTo.
- Отрицательное, нулевое и положительное значения означают соответственно меньше, равно и больше.
- Это позволяет использовать
List.Sort,Array.Sortи упорядоченные коллекции. - При совпадении сравнивайте вторичные поля.
- Следите, чтобы
CompareTo == 0соответствовалоEquals.
Часто задаваемые вопросы
Урок «Реализация IComparable» бесплатный?
Да — полный текст урока «Реализация IComparable» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Реализация IComparable»?
Определяйте естественный порядок сортировки с помощью CompareTo. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Реализация IComparable»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Реализация IEquatable
- Переопределение GetHashCode
- Реализация IComparable
- IComparer для пользовательской сортировки