Урок 11. Перегрузка реляционного оператора C#


Одиннадцатый урок учебника по объектно-ориентированному программированию на языке C# посвящен исследованию перегрузки операторов. На этот раз описана перегрузка реляционных операторов, позволяющая включать пользовательские классы в операции сравнения.

Реляционные операторы

Реляционные операторы позволяют сравнивать два значения или объекта и определять, равны ли они, или одно из них больше или меньше другого. Существует шесть реляционных операторов, которые могут быть напрямую перегружены для класса. Это операторы равенства (==) и неравенства (! =), больше (>), меньше (<), больше или равно (> =) и меньше или равно (<=).

Шесть операторов должны рассматриваться как три связанные пары. Когда одна из трех пар перегружена, противоположная версия также должна быть перегружена, чтобы класс мог быть скомпилирован без ошибок.

В этой статье используется класс Vector, разработанный в предыдущих статьях. Класс Vector представляет собой двумерный вектор и уже перегружает арифметические операторы, операторы true и false и логические операторы. Если у вас нет кода, скачайте его по ссылке выше.

Перегрузка операторов равенства и неравенства

Синтаксис

Операторы равенства и неравенства являются бинарными операторами и, как таковые, переопределяются с использованием того же синтаксиса, что и другие основные двоичные операторы. Следующий синтаксис для оператора равенства:

public static result-type operator ==(
    op-type operand,
    op-type2 operand2
)
Тип результата - это тип данных для возвращаемого значения операции. В большинстве случаев результатом будет логическое значение. Может использоваться другой тип возврата, но это ограничит функциональность оператора. По крайней мере один из двух операндов должен иметь тот же тип данных, что и содержащий класс. Изменяя тип данных одного из операндов, можно создать несколько перегруженных версий оператора и использовать их для сравнения объектов разных типов.

Функциональное воздействие

Перегрузка оператора равенства в корне меняет способ сравнения объектов. При обычных обстоятельствах сравнение двух объектов с использованием оператора равенства дает истинный результат, только если две сравниваемые переменные являются ссылками на один и тот же объект. то есть. Две ссылки на объекты указывают на одну и ту же область в памяти. Это можно продемонстрировать с помощью экземпляров класса Vector и метода Main проекта VectorDemo, как показано в следующем примере. Обратите внимание, что хотя векторы v1 и v2 имеют одинаковое значение, они не считаются равными.

static void Main(string[] args)
{
    Vector v1 = new Vector(3, 4);
    Vector v2 = new Vector(3, 4);
    Vector v3 = v2;
 
    Console.WriteLine(v2 == v1);                //  "False"
    Console.WriteLine(v3 == v2);                //  "True"
}
Когда вы перегружаете оператор равенства, вы можете изменить сравнение, сделав его прямым тестом на совпадение двух ссылок на объекты, и проверкой того, что объекты функционально одинаковы по значению. Эта концепция используется в .NET Framework для предоставления строковых объектов. Для строк оператор равенства перегружен, так что выполняется сравнение значений двух строк, а не их указателей на объекты.

Перегрузка операторов равенства и неравенства в классе векторов

Используя описанный выше синтаксис, мы можем добавить перегруженные операторы равенства и неравенства в класс Vector. Операции проверят, совпадают ли координаты X и Y двух сравниваемых векторов, прежде чем считать объекты равными. Чтобы перегрузить два оператора, добавьте следующий код в класс Vector:

public static bool operator ==(Vector v1, Vector v2)
{
    return (v1.X == v2.X && v1.Y == v2.Y);
}
 
public static bool operator !=(Vector v1, Vector v2)
{
    return (v1.X != v2.X || v1.Y != v2.Y);
}
Эти модификации изменяют сравнения так, что результаты предыдущего метода Main отличаются. Запустите программу еще раз, чтобы увидеть, что оператор равенства теперь сравнивает значения в векторе, а не ссылки на объекты. Обратите внимание, что векторы v1 и v2 теперь считаются равными.

static void Main(string[] args)
{
    Vector v1 = new Vector(3, 4);
    Vector v2 = new Vector(3, 4);
    Vector v3 = v2;
 
    Console.WriteLine(v2 == v1);                // "True"
    Console.WriteLine(v3 == v2);                // "True"
}
Метод Equals

Каждый класс автоматически наследует метод Equals. Этот метод выполняет сравнение двух значений или объектов, возвращая логическое значение, указывающее, совпадают ли эти два элемента. Для объектов ссылочного типа, таких как класс Vector, выполняется сравнение двух ссылок на объекты. После того, как оператор равенства был перегружен, результаты == и метод Equals больше не совпадают:

static void Main(string[] args)
{
    Vector v1 = new Vector(3, 4);
    Vector v2 = new Vector(3, 4);
    Vector v3 = v2;
 
    Console.WriteLine(v2 == v1);                //  "True"
    Console.WriteLine(v3 == v2);                //  "True"
    Console.WriteLine(v2.Equals(v1));           //  "False"
    Console.WriteLine(v3.Equals(v2));           //  "True"
}  
Обычно вы хотите изменить функциональность метода Equals, чтобы он соответствовал функциональности оператора равенства. Это достигается путем переопределения метода; техника, которая будет описана в следующей статье этого урока. После изменения метода Equals можно проверить, что ссылки на два объекта совпадают, используя статический метод ReferenceEquals.

Метод GetHashCode

Метод GetHashCode - это еще один член, который необходимо учитывать при перегрузке оператора равенства для класса. Этот метод использует алгоритм хеширования для генерации целочисленного хеш-кода для объекта. Сгенерированный хеш-код может использоваться для многих целей, в том числе в коллекции Hashtable.

Реализация по умолчанию для метода GetHashCode создает значение хеш-кода, которое может различаться для двух функционально эквивалентных объектов. Как и в случае с методом Equals, обычно при перегрузке оператора равенства вы захотите переопределить метод GetHashCode. Созданные хэш-коды могут быть основаны на значении базового объекта, а не на ссылке на объект.

Примечание. Предупреждающие сообщения генерируются компилятором, когда оператор равенства перегружен, но методы Equals и GetHashCode не переопределяются. Эти предупреждения не следует игнорировать, но они не будут препятствовать компиляции или выполнению кода.

Перегрузка операторов сравнения

Операторы сравнения перегружены с использованием того же основного синтаксиса, что и оператор равенства. Опять же, операторы должны быть перегружены попарно. то есть, если <перегружено, то должно быть и>, а если <= перегружено> = также должно быть.

Перегрузка операторов сравнения в классе Vector

Чтобы определить, какой из двух векторных объектов больше или меньше, будут сравниваться длины двух векторов. Чтобы упростить это, мы сначала добавим новое, доступное только для чтения свойство в класс Vector. Это свойство будет использовать теорему Пифагора для вычисления длины вектора.

Добавьте новое свойство в класс Vector следующим образом. Этот код использует библиотеку System.Math для математических функций, поэтому убедитесь, что в начало файла кода входит using System; объявление.

public double Length
{
    get { return Math.Sqrt(_x * _x + _y * _y); }
}
Теперь, когда у нас есть свойство Length для облегчения сравнения векторов, мы можем определить четыре оставшихся реляционных оператора. Каждый из операторов выполнит сравнение длин двух векторов, переданных как операнды, и выдаст логический результат. Добавьте следующий код в класс Vector для перегрузки операторов:

public static bool operator >(Vector v1, Vector v2)
{
    return (v1.Length > v2.Length);
}
 
public static bool operator <(Vector v1, Vector v2)
{
    return (v1.Length < v2.Length);
}
 
public static bool operator >=(Vector v1, Vector v2)
{
    return (v1.Length >= v2.Length);
}
 
public static bool operator <=(Vector v1, Vector v2)
{
    return (v1.Length <= v2.Length);
}
Теперь вы можете протестировать новые операторы, настроив метод Main программы следующим образом:

static void Main(string[] args)
{
    Vector v1 = new Vector(3, 4);
    Vector v2 = new Vector(4, 3);
    Vector v3 = new Vector(3, 5);
 
    Console.WriteLine(v1 > v2);                 //  "False"
    Console.WriteLine(v1 < v2);                 //  "False"
    Console.WriteLine(v1 >= v2);                //  "True"
    Console.WriteLine(v1 <= v2);                //  "True"
    Console.WriteLine(v1 > v3);                 //  "False"
    Console.WriteLine(v1 < v3);                 //  "True"
    Console.WriteLine(v1 >= v3);                //  "False"
    Console.WriteLine(v1 <= v3);                //  "True"
}
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, си шарп, ООП




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.



Урок 22. hashcode и equals в Java: часть 1
Базовая аутентификация HTTP
Поиск клик графа по алгоритму Брона-Кербоша на C#