Указание множества порядков сортировки с помощью IComparer
В текущей версии класса
Car
в качестве основы для порядка сортировки используется идентификатор автомобиля (
CarID
). В другом проектном решении основой сортировки могло быть дружественное имя автомобиля (для вывода списка автомобилей в алфавитном порядке). А что если вы хотите построить класс
Car
, который можно было бы подвергать сортировке по идентификатору и также по дружественному имени? В таком случае вы должны ознакомиться с еще одним стандартным интерфейсом по имени
IComparer
, который определен в пространстве имен
System.Collections
следующим образом:
// Общий способ сравнения двух объектов.
interface IComparer
{
int Compare(object o1, object o2);
}
На заметку! Обобщенная версия этого интерфейса (
IСоmраrаble<Т>
) обеспечивает более безопасный в отношении типов способ обработки операций сравнения объектов. Обобщения подробно рассматриваются в главе 10.
В отличие от
IСоmраrаble
интерфейс
IComparer
обычно не реализуется в типе, который вы пытаетесь сортировать (т.е.
Car
). Взамен данный интерфейс реализуется в любом количестве вспомогательных классов, по одному для каждого порядка сортировки (на основе дружественного имени, идентификатора автомобиля и т.д.). В настоящий момент типу
Car
уже известно, как сравнивать автомобили друг с другом по внутреннему идентификатору. Следовательно, чтобы позволить пользователю объекта сортировать массив объектов
Car
по дружественному имени, потребуется создать дополнительный вспомогательный класс, реализующий интерфейс
IComparer
. Вот необходимый код (не забудьте импортировать в файл кода пространство имен
System.Collections
):
using System;
using System.Collections;
namespace ComparableCar
{
// Этот вспомогательный класс используется для сортировки
// массива объектов Car по дружественному имени.
public class PetNameComparer : IComparer
{
// Проверить дружественное имя каждого объекта.
int IComparer.Compare(object o1, object o2)
{
if (o1 is Car t1 && o2 is Car t2)
{
return string.Compare(t1.PetName, t2.PetName,
StringComparison.OrdinalIgnoreCase);
}
else
{
throw new ArgumentException("Parameter is not a Car!");
// Параметр не является объектом типа Car!
}
}
}
}
Вспомогательный класс
PetNameComparer
может быть задействован в коде. Класс
System.Array
содержит несколько перегруженных версий метода
Sort
, одна из которых принимает объект, реализующий интерфейс
IComparer
:
...
//
Теперь сортировать по дружественному имени.
Array.Sort(myAutos, new PetNameComparer);
// Вывести отсортированный массив.
Console.WriteLine("Ordering by pet name:");
foreach(Car c in myAutos)
{
Console.WriteLine("{0} {1}", c.CarID, c.PetName);
}
...
Специальные свойства и специальные типы сортировки
Важно отметить, что вы можете применять специальное статическое свойство, оказывая пользователю объекта помощь с сортировкой типов
Car
по специфичному элементу данных. Предположим, что в класс
Car
добавлено статическое свойство только для чтения по имени
SortByPetName
, которое возвращает экземпляр класса, реализующего интерфейс
IComparer
(в этом случае
PetNameComparer
; не забудьте импортировать пространство имен
System.Collections
):
// Теперь мы поддерживаем специальное свойство для возвращения
Теперь в коде массив можно сортировать по дружественному имени, используя жестко ассоциированное свойство, а не автономный класс
PetNameComparer
:
// Сортировка по дружественному имени становится немного яснее.
Array.Sort(myAutos, Car.SortByPetName);
К настоящему моменту вы должны не только понимать способы определения и реализации собственных интерфейсов, но также оценить их полезность. Конечно, интерфейсы встречаются внутри каждого важного пространства имен .NET Core, а в оставшихся главах книги вы продолжите работать с разнообразными стандартными интерфейсами.
Резюме
Интерфейс может быть определен как именованная коллекция абстрактных членов. Интерфейс общепринято расценивать как поведение, которое может поддерживаться заданным типом. Когда два или больше число типов реализуют один и тот же интерфейс, каждый из них может трактоваться одинаковым образом (полиморфизм на основе интерфейсов), даже если типы определены в разных иерархиях.
Для определения новых интерфейсов в языке C# предусмотрено ключевое слово
interface
. Как было показано в главе, тип может поддерживать столько интерфейсов, сколько необходимо, и интерфейсы указываются в виде списка с разделителями-запятыми. Более того, разрешено создавать интерфейсы, которые являются производными от множества базовых интерфейсов.
В дополнение к построению специальных интерфейсов библиотеки .NET Core определяют набор стандартных (т.е. поставляемых вместе с платформой) интерфейсов. Вы видели, что можно создавать специальные типы, которые реализуют предопределенные интерфейсы с целью поддержки набора желательных возможностей, таких как клонирование, сортировка и перечисление.
Глава 9
Время существования объектов
К настоящему моменту вы уже умеете создавать специальные типы классов в С#. Теперь вы узнаете, каким образом исполняющая среда управляет размещенными экземплярами классов (т.е. объектами) посредством сборки мусора. Программистам на C# никогда не приходится непосредственно удалять управляемый объект из памяти (вспомните, что в языке C# даже нет ключевого слова наподобие
delete
). Взамен объекты .NET Core размещаются в области памяти, которая называется управляемой кучей, где они автоматически уничтожаются сборщиком мусора "в какой-то момент в будущем".