- •Тема 6. Основы программирования на языке c#
- •Условная операция
- •Операции checked и unchecked
- •Операция поглощения null
- •Безопасность типов
- •Преобразования типов
- •Неявные преобразования
- •Явные преобразования
- •Упаковка и распаковка
- •Проверка равенства объектов
- •Виртуальный метод Equals()
- •Статический метод Equals()
- •Проверка типов значений на равенство
- •Перегрузка операций
- •Как работают операции
- •Пример перегрузки операции: структура Vector
- •Добавление дополнительных перегрузок
- •Перегрузка операций сравнения
- •Пользовательские приведения
- •Реализация пользовательских приведений
- •Приведение между классами
- •Приведение между базовым и производным классами
- •Упаковывающие и распаковывающие приведения
- •Множественные приведения
Перегрузка операций сравнения
В C# имеется шесть операций сравнения, которые мы уже представили ранее в этой главе (в разделе “Операции”), и их можно разделить на три пары:
1) == и !=
2) > и <
3) >= и <=
Язык C# требует, чтобы перегрузка этих операций выполнялась попарно. То есть, если вы перегружаете ==, то обязаны также перегрузить !=, иначе получите ошибку компиляции. К тому же операции сравнения должны возвращать bool. Это фундаментальное отличие операций сравнения от арифметических операций. Так, например, результат сложения или вычитания двух величин теоретически может быть любого типа. И вы уже видели, что умножение двух Vector может быть реализовано так, что возвращается скаляр. Другой пример касается базового класса .NET System.DateTime. Можно вычесть один экземпляр DateTime из другого, но результатом будет не DateTime, a System.TimeSpan. В отличие от этого, для операций сравнения не имеет смысла возвращать что-то, отличное от bool.
Если вы перегружаете == и !=, то должны также переопределить методы Equals() и GetHashCode() из System.Object, иначе получите от компилятора предупреждение. Причина в том, что метод Equals() должен реализовывать ту же логику сравнения, что и операция ==.
Помимо этого отличия, в остальном перегрузка операций сравнения следует тем же принципам, что и перегрузка арифметических операций. Однако сравнение величин не всегда так просто, как может показаться на первый взгляд. Например, если вы просто сравните две объектные ссылки, то при этом просто сравните два адреса памяти, где расположены объекты. Это редко бывает тем, что требуется от операции сравнения, а потому вы должны закодировать ее так, чтобы она сравнивала значения объектов и возвращала соответствующее булевское значение. В следующем примере переопределяются операции == и != для структуры Vector. Ниже показана реализация ==.
public static bool operator== (Vector lhs, Vector rhs)
{
if (lhs.x == rhs.x SS lhs.у == rhs.у && lhs.z == rhs.z)
return true;
else
return false;
}
В этом случае просто сравниваются два объекта Vector на предмет равенства значений соответствующих компонентов. Для большинства структур это именно то, что нужно, хотя иногда приходится тщательно обдумывать, что должно подразумеваться под равенством.
Например, если есть встроенный класс, должны ли вы просто сравнивать ссылки на предмет того, указывают они на один и тот же объект (неглубокое сравнение), или же на предмет равенства значений объектов, на которые установлены ссылки (глубокое сравнение)}
Неглубокое сравнение это когда проверяется, ссылаются ли объекты на одну и ту же область памяти, в то время как глубокое сравнение работает со значениями и свойствами объекта с целью определения эквивалентности. Вы должны выполнять проверку эквивалентности в зависимости от глубины; это поможет решить, что именно вы хотите проверить.
Не поддавайтесь соблазну перегружать операцию сравнения, вызывая метод Equals(), унаследованный от System.Object. Если сделать так, а потом попробовать вычислить (objA== objВ), когда objA содержит null, то исполняющая среда .NET попытается выполнить null.Equals(objB). Обратный вариант (переопределение Equals() для вызова операции сравнения) должен быть безопасен.
Также придется перегрузить операцию !=. Ниже показан простейший способ, как это сделать:
public static bool operator!= (Vector lhs, Vector rhs)
{
return !(lhs == rhs);
}
Как обычно, стоит сразу проверить, работает ли полученная перегрузка, написав некоторый тестовый код. На этот раз определим три объекта Vector и сравним их:
static void Main()
{
Vector vect1, vect2, vect3;
Vect1 = new Vector (3.0,3.0,-10.0);
vect2 = new Vector(3.0,3.0,-10.0);
vect3 = new Vector(2.0,3.0,6.0);
Console.WriteLine("vect1==vect2 возвращает " + (vect1==vect2));
Console.WriteLine("vect1==vect3 возвращает " + (vect1==vect3));
Console.WriteLine('vect2==vect3 возвращает " + (vect2==vect3));
Console.WriteLine();
Console.WriteLine("vect1!=vect2 возвращает " + (vect1!=vect2));
Console.WriteLine("vect1!=vect3 возвращает " + (vect1!=vect3));
Console.WriteLine("vect2!=vect3 возвращает " + (vect2!=vect3));
}
При компиляции этого кода генерируется показанное ниже предупреждение компилятора, поскольку для структуры Vector не был переопределен метод Equals(). В данном случае это не имеет значения, поэтому его можно проигнорировать,
сsc Vectors3.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.21006.1
for Microsoft (R) .NET Framework version 4.0
Copyright (C) Microsoft Corporation. All rights reserved.
Vectors3.cs(5,11) : warning CS0660: 'Wrox.ProCSharp.OOCSharp.Vector’
defines operator == or operator != but does not override
Object.Equals(object o)
Vectors3.cs(5,11) : warning CS0661: 'Wrox.ProCSharp.OOCSharp.Vector'
defines operator == or operator != but does not override
Object.GetHashCode()
Vectors3.cs (5,11) : Предупреждение CS0660: 'Wrox.ProCSharp.OOCSharp.
Vector' определяет операцию == или != но не переопределяет
Object.Equals(object о)
Vectors3.cs (5,11): предупреждение CS0661: 'Wrox.ProCSharp.
OOCSharp.Vector' определяет операцию == или !=, но не переопределяет
Object.GetHashCode()
В результате запуска этого примера на экране появляется следующий вывод:
vect1==vect2 возвращает True
vect1==vect3 возвращает False
vect2==vect3 возвращает False
vect1!=vect2 возвращает False
vect1!=vect3 возвращает True
vect2!=vect3 возвращает True
Какие операции можно перегружать?
Перегружать можно не все доступные операции. Те из них, которые перегружать можно, перечислены в табл. 7.6.
Таблица 7.6. Операции, которые разрешено перегружать
Категория |
Операции |
Ограничения |
Арифметические бинарные |
+,-,*,/,% |
Нет |
Арифметические унарные |
+,-,++,-- |
Нет |
Битовые бинарные |
&,|,^,<<,>> |
Нет |
Битовые унарные |
!,~ true, false |
Операции true и false должны перегружаться в паре |
Сравнение |
==,!= <=,>= <,> |
Операции сравнения должны перегружаться попарно |
Присваивание |
+=,-=,*=,/=,>>=, <<=,%=,&=,|=,^= |
Эти операции нельзя явно перегрузить; их перегрузка осуществляется неявно при переопределении индивидуальных операций +, -, % и т. д. |
Индексация |
[] |
Операцию индекса нельзя перегрузить явно. Тип индексирующего члена, описанный в разделе 6.2, позволяет поддерживать операцию индексации в пользовательских классах и структурах |
Приведение |
() |
Операцию приведения нельзя перегрузить явно. Пользовательские приведения (описанные ниже) позволяют реализовать настраиваемые приведения |
