Файл: Сравнение и сортировка объектов произвольного типа Сравнение произвольных типов в C#. Интерфейс icomparable.docx
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 22.11.2023
Просмотров: 20
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
Сравнение и сортировка объектов произвольного типа
Сравнение произвольных типов в C#. Интерфейс IComparable
Как нам уже известно, для сравнения переменных примитивных типов мы можем использовать различные логические операторы. Однако, при написании своих собственных программ мы далеко не всегда используем только примитивные типы данных – мы можем создавать собственные классы с произвольными наборами свойств. И, в этом случае, мы уже не можем просто воспользоваться логическими операторами для сравнения, так как платформа .NET просто не знает, как сравнивать произвольные классы (и можно ли их сравнивать, в принципе). Для сравнения двух объектов произвольного типа мы можем, например, воспользоваться возможностью перегрузки операторов, а можем использовать интерфейс IComparable.
Пример класса для сравнения.
Пусть у нас имеется класс, описывающий обычную коробку:
class Box
{
public double Width { get; set; }
public double Height { get; set; }
public double Length { get; set; }
public double Volume()
{
return Width * Height * Length;
}
}
У класса также определен метод Volume, возвращающий объем нашей коробки. Как определить, что один объект типа Box больше другого или равен? Условимся, что «больше» будет та коробка, у которой больше объем. Теперь попробуем сделать так, чтобы платформа .NET «научилась» сравнивать коробки.
Интерфейс IComparable
Интерфейс IComparable определяет метод сравнения, который реализуется типом значения или классом для упорядочения или сортировки экземпляров и содержит всего лишь один метод – CompareTo, который должен возвращать число:
Меньше нуля | Если текущий экземпляр предшествует (меньше) объекта, указанного в методе CompareTo |
Нуль | Если текущий экземпляр находится в том же положении (равен) объекту, указанному в методе CompareTo. |
Больше нуля | Если текущий экземпляр следует за объектом (больше), заданным в методе CompareTo. |
Чаще всего, при реализации метода CompareTo используются числа 1, 0 и -1. Реализуем интерфейс IComparable в нашем классе Box:
class Box: IComparable
{
public double Width { get; set; }
public double Height { get; set; }
public double Length { get; set; }
public double Volume()
{
return Width * Height * Length;
}
public int CompareTo(object obj)
{
Box box = obj as Box;
if (box == null)
throw new Exception("Невозможно сравнить объекты");
if (this.Volume() > box.Volume())
return 1;
if (this.Volume() < box.Volume())
return -1;
return 0;
}
}
Выше приведен подробный код реализации метода CompareTo. Но, так как результатом метода Volume является примитивный тип double, то мы можем написать наш метод намного короче, используя метод CompareTo() уже реализованный в типе double:
public int CompareTo(object obj)
{
Box box = obj as Box;
if (box == null)
throw new Exception("Невозможно сравнить объекты");
return Volume().CompareTo(box.Volume());
}
Теперь мы можем сравнивать объекты типа Box, например:
class Program
{
static void Main(string[] args)
{
Box box1 = new Box() { Height = 1, Length = 1, Width = 1 };
Box box2 = new Box() { Height = 2, Length = 2, Width = 1 };
Console.WriteLine("Объем первой коробки {0}", box1.Volume());
Console.WriteLine("Объем второй коробки {0}", box2.Volume());
if (box1.CompareTo(box2) > 0)
Console.WriteLine("Первая коробка БОЛЬШЕ второй");
else
if (box1.CompareTo(box2) < 0)
Console.WriteLine("Первая коробка МЕНЬШЕ второй");
else
Console.WriteLine("Первая коробка РАВНА второй");
Console.ReadLine();
}
}
Результат работы программы:
Объем первой коробки 1
Объем второй коробки 4
Первая коробка МЕНЬШЕ второй
Интерфейс IComparable
Интерфейс IComparable имеет также обобщенную версию – IComparable
class Box : IComparable
{
... // свойства и методы класса
public int CompareTo(Box box)
{
if (box == null)
throw new Exception("Невозможно сравнить объекты");
//Используем метод CompareTo у типа double
return Volume().CompareTo(box.Volume());
}
}
Как можно видеть, метод CompareTo обобщенной версии интерфейса в качестве параметра принимает объект, типа, указанного в описании, т.е. в нашем случае – типа Box, что позволяет нам избежать лишнего приведения типов.
Итог
Интерфейс IComparable предназначен для реализации в классах метода сравнения двух объектов. Этот интерфейс удобно использовать в том случае, если заранее не известно, как сравнить два объекта произвольного типа. Обобщенная версия интерфейса —IComparable
Сортировка объектов произвольного типа. Интерфейс IComparer
Для чего нужен интерфейс IComparer
У обобщенного списка List
class Program
{
static void Main(string[] args)
{
List
{
new Box(){Height = 2, Length = 2, Width = 2 },//Объем 8
new Box(){Height = 1, Length = 1, Width = 1 },//Объем 1
new Box(){Height = 3, Length = 3, Width = 3 },//Объем 27
new Box(){Height = 4, Length = 4, Width = 4 } //Объем 64
};
boxes.Sort();
foreach (Box box in boxes)
{
Console.WriteLine("Длина {0} Ширина {1} Высота {2} Объем {3}",
box.Length, box.Width, box.Height, box.Volume());
}
Console.ReadLine();
}
}
Так как наш класс реализует интерфейс IComparable, то платформа .NET «понимает» как сравнивать два объекта типа Box между собой и, поэтому, результат сортировки будет вполне ожидаемым – все коробки отсортируются по их объему:
Длина 1 Ширина 1 Высота 1 Объем 1
Длина 2 Ширина 2 Высота 2 Объем 8
Длина 3 Ширина 3 Высота 3 Объем 27
Длина 4 Ширина 4 Высота 4 Объем 64
Но что, если нам потребуется провести сортировку наших коробок не по объему, а, например, по их длине? Каждый раз переписывать реализацию метода CompareTo – не вариант. В этом случае нам поможет интерфейс IComparer, реализуя который в классе мы можем впоследствии переопределять метод сортировки в списке List
Интерфейс IComparer
Интерфейс IComparer определяет метод Compare, с помощью которого проводится сравнение двух элементов. Как и в случае с методом CompareTo, метод Compare возвращает три значения:
Меньше нуля | Если левый элемент предшествует (меньше) правого, указанного в методе Compare |
Нуль | Если элементы равны |
Больше нуля | Если левый элемент следует за объектом (больше), заданным в методе Compare |
Попробуем реализовать метод сортировки коробок по их длине. Для этого создадим новый класс BoxComparer и реализуем в нем интерфейс IComparer:
class BoxComparer : IComparer
{
public int Compare(Box x, Box y)
{
if ((x == null) || (y == null))
throw new Exception("Невозможно сравнить элементы");
return Math.Sign(x.Length - y.Length);
}
}
Теперь можем воспользоваться нашим классом-компаратором и отсортировать коробки по длине:
List
{
new Box(){Height = 3, Length = 2, Width = 8 },
new Box(){Height = 1, Length = 1, Width = 1 },
new Box(){Height = 5, Length = 3, Width = 4 },
new Box(){Height = 1, Length = 4, Width = 1 }
};
boxes.Sort(new BoxComparer());
foreach (Box box in boxes)
{
Console.WriteLine("Длина {0} Ширина {1} Высота {2} Объем {3}",
box.Length, box.Width, box.Height, box.Volume());
}
Результат:
Длина 1 Ширина 1 Высота 1 Объем 1
Длина 2 Ширина 8 Высота 3 Объем 48
Длина 3 Ширина 4 Высота 5 Объем 60
Длина 4 Ширина 1 Высота 1 Объем 4
Используя класс BoxComparer, мы можем реализовать в нем разные методы сортировки, но, при этом, метод Compare должен быть одним. Например, реализуем сортировку по возрастанию и убыванию длины:
class BoxComparer : IComparer
{
private bool ascSort = true;
public bool AscSort
{
get { return ascSort; }
set { ascSort = value; }
}
public int Compare(Box x, Box y)
{
if ((x == null) || (y == null))
throw new Exception("Невозможно сравнить элементы");
if (AscSort)
return Math.Sign(x.Length - y.Length);
else
return Math.Sign(y.Length - x.Length);
}
}
Мы добавили свойство AscSort, указывающее на то, как сортировать список. Пример использования:
class Program
{
static void Main(string[] args)
{
List
{
new Box(){Height = 3, Length = 2, Width = 8 },
new Box(){Height = 1, Length = 1, Width = 1 },
new Box(){Height = 5, Length = 3, Width = 4 },
new Box(){Height = 1, Length = 4, Width = 1 }
};
Console.WriteLine("Сортировка коробок по ВОЗРАСТАНИЮ длины");
boxes.Sort(new BoxComparer());
foreach (Box box in boxes)
{
Console.WriteLine("Длина {0} Ширина {1} Высота {2} Объем {3}",
box.Length, box.Width, box.Height, box.Volume());
}
Console.WriteLine("Сортировка коробок по УБЫВАНИЮ длины");
boxes.Sort(new BoxComparer() { AscSort = false });
foreach (Box box in boxes)
{
Console.WriteLine("Длина {0} Ширина {1} Высота {2} Объем {3}",
box.Length, box.Width, box.Height, box.Volume());
}
Console.ReadLine();
}
}
Результат:
Сортировка коробок по ВОЗРАСТАНИЮ длины
Длина 1 Ширина 1 Высота 1 Объем 1
Длина 2 Ширина 8 Высота 3 Объем 48
Длина 3 Ширина 4 Высота 5 Объем 60
Длина 4 Ширина 1 Высота 1 Объем 4
Сортировка коробок по УБЫВАНИЮ длины
Длина 4 Ширина 1 Высота 1 Объем 4
Длина 3 Ширина 4 Высота 5 Объем 60
Длина 2 Ширина 8 Высота 3 Объем 48
Длина 1 Ширина 1 Высота 1 Объем 1
Итог
Интерфейс IComparer позволяет переопределить способ сортировки элементов коллекции. Для сортировки элементов коллекции, объекты должны реализовывать интерфейс IComparable, который устанавливает правила сравнения двух элементов (метод CompareTo). Если у коллекции вызывается метод Sort() без параметров, то используется метод сравнения по умолчанию, т.е. CompareTo. Интерфейс же IComparer позволяет создать свою логику сравнения и сортировки объектов произвольного типа, отличную от реализованной в методе CompareTo.
Интернет-источники
-
Сравнение произвольных типов в C#. Интерфейс IComparable
https://csharp.webdelphi.ru/sravnenie-proizvolnyx-tipov-v-c-interfejs-icomparable/
-
Сортировка объектов произвольного типа. Интерфейс IComparer
https://csharp.webdelphi.ru/sortirovka-obektov-proizvolnogo-tipa-interfejs-icomparer/