На этом шаге мы приведем общие сведения о механизме перегрузки операторов.
Как мы уже знаем, в языке C# есть много различных операторов, которые позволяют выполнять всевозможные операции со значениями разных типов. Например, мы можем с помощью оператора "плюс" + сложить два числа и получить в результате число. Мы можем "сложить" с помощью того же оператора два текстовых значения и получить в результате текст. Существуют и другие ситуации, когда мы можем использовать оператор + (или какой-нибудь иной оператор). Все они предопределены: то есть случаи, когда мы можем использовать тот или иной оператор, определены заранее и зависят от типа операндов, участвующих в операции. Другими словами, имеется ограниченное количество ситуаций, в которых могут использоваться операторы языка С#, а результат выполнения операций зависит от типа операндов. Например, мы можем к тексту прибавить число, но не можем текст умножить на число. В этом смысле наши возможности ограничены и строго детерминированы. Но есть в языке C# очень мощный и эффективный механизм, который связан с перегрузкой операторов. Основная идея в том, что для объектов пользовательских классов (то есть классов, которые мы описываем в программе) доопределяется действие встроенных операторов языка С#. Скажем, описывая класс, возможно путем несложных манипуляций добиться того, что объекты этого класса можно будет складывать, как числа. Или, например, к объекту класса можно будет прибавить число, и так далее. Смысл каждой такой операции определяем мы, когда описываем класс.
Чтобы определить способ применения того или иного оператора к объектам класса, в классе описывается специальный метод, который называется операторным. Это метод, но немножко специфический. Название операторного метода состоит из ключевого слова operator и символа оператора, для которого определяется способ применения к объектам класса. Например, если мы хотим определить операцию сложения для объектов класса с помощью оператора +, то в классе нужно описать операторный метод под названием operator+. Если в классе описан операторный метод с названием operator*, то для объектов класса определена операция "умножения": к таким объектам можно применять оператор умножения *. Ну и так далее.
Перегружать можно не все операторы. Доступные для перегрузки операторы и особенности перегрузки некоторых операторов обсуждаются в этом и следующих шагах.
Но мало знать название операторного метода. Есть некоторые требования, которым должны соответствовать операторные методы. Вот они.
Допустим, имеется класс, в котором описан операторный метод с названием operator+. А еще есть выражение вида А+В, в котором хотя бы один из операндов А и В является объектом упомянутого класса.
Тогда для вычисления результата выражения А+В вызывается операторный метод с названием operator+. При этом операнд А интерпретируется как первый аргумент операторного метода, а операнд В передается
вторым аргументом операторному методу.
Выше речь шла о бинарном операторе +. У бинарного оператора два операнда (в выражении А+В операнды - это А и В). Но бывают операторы унарные, у которых один операнд. Примером унарного оператора может быть инкремент ++ или декремент --. Если обрабатывается выражение вида А++ и А является объектом класса, в котором описан операторный метод с названием operator++, то именно этот метод будет вызван для обработки выражения А++. Поскольку операторный метод определен для унарного оператора, то у метода всего один аргумент. Этим аргументом методу будет передан операнд А.
В языке C# перегружаться могут многие операторы, но не все. Например, среди бинарных операторов для перегрузки доступны такие: +, -, *, /, %, &, |, ^, << и >>. Также можно перегружать следующие унарные операторы: +, -, !, ~, ++, --, true и false. Несложно заметить, что выше перечислены в основном арифметические и побитовые операторы.
Операторы "плюс" + и "минус" - упомянуты и среди бинарных, и среди унарных. Ошибки здесь нет, поскольку данные операторы могут использоваться и как унарные, и как бинарные. В выражениях вида
А+В и A-В операторы используются как бинарные. Но еще операторы "плюс" и "минус" могут использоваться в следующем формате: +А или -А. Здесь речь идет об
унарных операторах. С унарным оператором "минус" мы сталкиваемся каждый раз, когда записываем отрицательные числа: унарный оператор "минус" служит индикатором отрицательного числа. Унарный оператор "плюс"
может использоваться как индикатор положительного числа. Но на практике он обычно не используется. Тем не менее, если мы имеем дело с объектами, то вполне можем использовать с этими объектами не только бинарные,
но и унарные операторы "плюс" и "минус". Для этого достаточно в соответствующем классе описать операторные методы для указанных операторов.
Отдельного внимания заслуживают операторы true и false. Речь идет о том, что можно определить способ проверки объекта на предмет "истинности" или "ложности". Объекты, для которых определена такая проверка (перегружены операторы true и false), могут использоваться в качестве условий в условных и циклических конструкциях. Операторы true и false перегружаются только в паре: или оба, или ни одного. Перегрузке этих операторов будет посвящен отдельный шаг.
Перегружать можно и операторы сравнения, но такие операторы должны перегружаться парами: == и !=, < и >, <= и >=. Также в языке С# есть специальные операторы приведения типа, которые позволяют задавать закон преобразования объекта одного типа в объект другого типа. В пользовательском классе можно описать оператор явного приведения типа (explicit-форма) или неявного приведения типа (implicit-форма).
Не перегружаются сокращенные операторы присваивания, такие как +=, -=, *=, /=, %=, &=, |=, ^=, <<= и >>=. Но при этом мы можем так переопределить базовые операторы (имеются в виду бинарные операторы +, -, *, /, %, &, |, ^, << и >>), что удастся использовать и сокращенные формы оператора присваивания. Похожая ситуация имеет место с логическими операторами && и || - они не перегружаются. Однако если "правильным образом" перегрузить операторы & и |, то в программном коде к операндам, являющимся объектами класса с перегруженными операторами & и |, можно будет применять и операторы && и ||.
Ситуацию с "правильной перегрузкой" операторов & и | (для возможности использования операторов && и ||) мы рассмотрим отдельно.
Есть операторы, которые не перегружаются. Среди них:
Перегрузка операторов подразумевает, что для одного бинарного оператора может описываться несколько операторных методов, которые отличаются типом аргументов и, соответственно, которые обрабатывают выражения (на основе данного оператора)
с операндами разного типа.
Далее мы более подробно и на конкретных примерах рассмотрим способы перегрузки разных групп операторов.
На следующем шаге мы рассмотрим перегрузку арифметических и побитовых операторов.