Создание сравнимых объектов (IComparable)
Создание сравнимых объектов (IComparable)
Интерфейс System.IComparable определяет поведение, позволяющее сортировать объекты по заданному ключу. Вот формальное определение.
// Этот интерфейс позволяет объекту указать его связь
// с другими подобными объектами.
public interface IComparable {
int CompareTo(object o);
}
Предположим теперь, что класс Car поддерживает некоторый внутренний идентификатор (представленный целым числом, хранимым в переменной carID), значение которого можно устанавливать с помощью параметра конструктора и изменять с помощью нового свойства ID. Ниже показана соответствующая модификация типа Car.
public class Car {
…
private int carID;
public int ID {
get { return carID; }
set { carID = value; }
}
public Car(string name, int currSp, int id) {
currSpeed = currSp;
petName = name;
carID = id;
}
…
}
Пользователи объекта могут создать массив типов Car так.
static void Main(string[] args) {
// Создание массива типов Car.
Car[] myAutos = new Car[5];
myAutos[0] = new Car("Rusty", 80, 1);
myAutos[1] = new Car("Mary", 40, 234);
myAutos[2] = new Car("Viper", 40, 34);
myAutos[3] = new Car("Mel", 40, 4);
myAutos[4] = new Car("Chucky", 40, 5);
}
Вспомним, что класс System.Array определяет статический метод Sort(). Вызвав этот метод для массива встроенных типов (int, short, string и т.д.), можно отсортировать элементы в массиве в числовом или алфавитном порядке, поскольку встроенные типы данных реализуют IComparable. Но что произойдет в том случае, когда методу Sort() будет передан массив типов Car, как показано ниже?
// Будут ли отсортированы мои автомобили?
Array.Sort(myAutos);
Запустив этот пример, вы обнаружите, что среда выполнения сгенерирует исключение ArgumentException c сообщением следующего содержания: "Как минимум один объект должен реализовать IComparable". Чтобы позволить сортировку массивов ваших пользовательских типов, вы должны реализовать IComparable. При создании CompareTo() вы должны решить, что должно лежать в основе соответствующей операции упорядочения. Для типа Car самым подходящим "кандидатом" является carID.
// Последовательность Car можно упорядочить на основе CarID.
public class Car: IComparable {
…
// Реализация IComparable.
int IComparable.CompareTo(object obj) {
Car temp = (Car)obj;
if (this.carID › temp.carID) return 1;
if(this.carID ‹ temp.carID) return -1;
else return 0;
}
}
Как видите, в CompareTo() выполняется сравнение поступившего типа с текущим экземпляром на основе сравнения значений заданных элементов данных. Возвращаемое значение CompareTo() указывает, будет ли данный тип меньше, больше или равен объекту сравнения (см. табл. 7.1).
Таблица 7.1. Возвращаемые значения CompareTo()
Возвращаемое значение Описание Любое число, меньшее нуля В данном порядке сортировки текущий экземпляр размещается до указанного объекта Нуль Этот экземпляр равен указанному объекту Любое число, большее нуля В данном порядке сортировки текущий экземпляр размещается после указанного объектаТеперь, когда тип Car "умеет" сравнивать себя с подобными объектами, вы можете записать пользовательский программный код следующего вида.
// Проверка интерфейса IComparable.
static void Main(string[] args) {
// Создание массива типов Car.
// Вывод исходного массива.
Console.WriteLine("Несортированный набор машин:");
foreach(Car с in myAutos) Console.WriteLine("{0) (1}", с.ID, с.petName);
// Теперь сортируем их с помощью IComparable.
Array.Sort(myAutos);
// Вывод отсортированного массива.
Console.WriteLine(" Упорядоченный набор машин:");
foreach(Car с in myAutos) Console.WriteLine("{0} {1}", с.ID, с.petName);
Console.ReadLine();
}
На рис. 7.10 показан соответствующий вывод.
Рис. 7.10. Сравнение автомобилей на основе значений ID