Шрифт:
Интервал:
Закладка:
Перегрузка операций эквивалентности
Как упоминалось в главе 6, метод System.Object.Equals() может быть перегружен для выполнения сравнений на основе значений (а не ссылок) между ссылочными типами. Если вы решили переопределить Equals() (часто вместе со связанным методом System.Object.GetHashCode()), то легко переопределите и операции проверки эквивалентности (== и !=). Взгляните на обновленный тип Point:
// В данной версии типа Point также перегружены операции == и !=.
public class Point
{
...
public override bool Equals(object o)
=> o.ToString() == this.ToString();
public override int GetHashCode()
=> this.ToString().GetHashCode();
// Теперь перегрузить операции == и !=.
public static bool operator ==(Point p1, Point p2)
=> p1.Equals(p2);
public static bool operator !=(Point p1, Point p2)
=> !p1.Equals(p2);
}
Обратите внимание, что для выполнения всей работы в реализациях операций == и != просто вызывается перегруженный метод Equals(). Вот как теперь можно применять класс Point:
// Использование перегруженных операций эквивалентности.
...
Console.WriteLine("ptOne == ptTwo : {0}", ptOne == ptTwo);
Console.WriteLine("ptOne != ptTwo : {0}", ptOne != ptTwo);
Console.ReadLine();
Как видите, сравнение двух объектов с использованием хорошо знакомых операций == и != выглядит намного интуитивно понятнее, чем вызов метода Object.Equals(). При перегрузке операций эквивалентности для определенного класса имейте в виду, что C# требует, чтобы в случае перегрузки операции == обязательно перегружалась также и операция != (компилятор напомнит, если вы забудете это сделать).
Перегрузка операций сравнения
В главе 8 было показано, каким образом реализовывать интерфейс IComparable для сравнения двух похожих объектов. В действительности для того же самого класса можно также перегрузить операции сравнения (<, >, <= и >=). Как и в случае операций эквивалентности, язык C# требует, чтобы при перегрузке операции < обязательно перегружалась также операция >. Если класс Point перегружает указанные операции сравнения, тогда пользователь объекта может сравнивать объекты Point:
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})// Использование перегруженных операций < и >.
...
Console.WriteLine("ptOne < ptTwo : {0}", ptOne < ptTwo);
Console.WriteLine("ptOne > ptTwo : {0}", ptOne > ptTwo);
Console.ReadLine();
Когда интерфейс IComparable (или, что еще лучше, его обобщенный эквивалент) реализован, перегрузка операций сравнения становится тривиальной. Вот модифицированное определение класса:
// Объекты Point также можно сравнивать посредством операций сравнения.
public class Point : IComparable<Point>
{
...
public int CompareTo(Point other)
{
if (this.X > other.X && this.Y > other.Y)
{
return 1;
}
if (this.X < other.X && this.Y < other.Y)
{
return -1;
}
return 0;
}
public static bool operator <(Point p1, Point p2)
=> p1.CompareTo(p2) < 0;
public static bool operator >(Point p1, Point p2)
=> p1.CompareTo(p2) > 0;
public static bool operator <=(Point p1, Point p2)
=> p1.CompareTo(p2) <= 0;
public static bool operator >=(Point p1, Point p2)
=> p1.CompareTo(p2) >= 0;
}
Финальные соображения относительно перегрузки операций
Как уже упоминалось, язык C# предоставляет возможность построения типов, которые могут уникальным образом реагировать на разнообразные встроенные хорошо известные операции. Перед добавлением поддержки такого поведения в классы вы должны удостовериться в том, что операции, которые планируется перегружать, имеют какой-нибудь смысл в реальности.
Например, пусть перегружена операция умножения для класса MiniVan, представляющего минивэн. Что по своей сути будет означать перемножение двух объектов MiniVan? В нем нет особого смысла. На самом деле коллеги по команде даже могут быть озадачены, когда увидят следующее применение класса MiniVan:
- Понимание SQL - Мартин Грубер - Базы данных