Типы и форматы перегруженных операций

Перегруженные операции определяются как общедоступные статические методы в классах, для расширения которых они предназначены. В зависимости от типа перегружаемой операции, такие методы могут принимать один или два параметра, и всегда возвращают значение. Для всех операций за исключением операций преобразования, типом одного из параметров должен быть тип включающего метод класса.

Например, не имеет смысла перегружать операцию + в классе Complex, если он будет складывать вместе два значения double. К тому же, как вы вскоре убедитесь, это даже невозможно.

Типичная операция + для класса Complex может выглядеть так:
public static Complex operator+( Complex lhs, Complex rhs )

Несмотря на то что этот метод складывает два экземпляра Complex, чтобы произвести третий экземпляр Complex, ничто не запрещает сделать один из параметров типа double, чтобы таким образом прибавлять double к экземпляру Complex. Как именно прибавлять double к экземпляру Complex — дело ваше. В общем случае синтаксис перегрузки операций следует приведенному шаблону, где + заменяется нужной операцией. Разумеется, некоторые операции принимают только один параметр.

При сравнении операций С# с операциями С++ обратите внимание, что объявления операций С# больше напоминают прием реализации перегруженных операций с помощью дружественных функций в С++, поскольку операции С# не являются методами экземпляра.

Существуют всего три группы перегружаемых операций.

• Унарные операции принимают только один параметр. К хорошо знакомым унарным операциям относятся ++ и —.

• Бинарные операции, как следует из их названия, принимают два параметра и включают знакомые математические операции, такие как +, -, / и *, а также операции сравнения.

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

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

Например, следующее объявление является некорректным:

1
2
3
4
5
6
7
8
9
public class Apple {
public static Apple operator+( Apple lhs, Apple rhs )  {
// Метод не делает ничего; создан только для примера, return rhs;
}
public class GreenApple : Apple {
// НЕВЕРНО!!! Компилироваться не будет.
public static Apple operator+( Apple lhs, Apple rhs )  {
// Метод не делает ничего; создан только для примера.
return rhs;

При попытке скомпилировать приведенный выше код будет получено следующее сообщение об ошибке:

error CS0563: One of the parameters of a binary operator must be the containing type
ошибка CS0563: Один из параметров бинарной операции должен иметь включающий тип

Операции не должны изменять свои операнды

Как уже известно, методы операций являются статическими. Поэтому настоятельно рекомендуется (требуется) не изменять передаваемые им параметры. Вместо этого необходимо создавать новый экземпляр возвращаемого типа и возвращать его в качестве результата операции.

Неизменяемые структуры и классы вроде System.String — блестящие кандидаты для реализации пользовательских операций. Такое поведение естественно для булевских операций, которые обычно возвращают тип, отличающийся от переданных типов.

“Минуточку!” — скажут некоторые из вас, принадлежащие к сообществу С++, — “а как же тогда реализовать постфиксные и префиксные операции ++ и — без изменения операнда?”.

Все операции С# являются статическими, и к ним относятся также и постфиксные, и префиксные операции, в то время как в С++ они представлены методами экземпляров, модифицирующими экземпляр объекта через указатель this.

Изящество подхода С# заключается в том, что беспокоиться о реализации двух различных версий операции ++, чтобы поддерживать префиксную и постфиксную его формы, как в С++, не понадобится. Задача создания временных копий объекта для обработки отличий в поведении префиксной и постфиксной форм решается компилятором.

Это еще одна причина того, что операции могут возвращать новые экземпляры, никогда не модифицируя состояния самих операндов. Если вы не будете следовать такой практике, то обречете себя на некоторые серьезные неприятности во время отладки.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100