На этом шаге мы рассмотрим перегрузку операторов == и !=.
Похожим образом обстоят дела с перегрузкой операторов "равно" == и "не равно" !=. Правда, есть и небольшие особенности. Чтобы понять их суть, необходимо сделать небольшое отступление.
Даже если мы не перегружаем операторы == и != объектов класса, мы все равно можем использовать эти операторы (в отличие от операторов <, >, <= и >=) для сравнения объектов. Например, если объекты А и В относятся к одному и тому же классу, то операции вида А==В и А!=B вполне законны и имеют смысл. По умолчанию, если операторы == и != не перегружались, при выполнении команды А==В проверяются на предмет совпадения значения объектных переменных A и B. А в эти переменные фактически записаны адреса объектов, на которые переменные ссылаются. Поэтому результатом выражения А==В является значение true, если переменные А и В ссылаются на один и тот же объект. Если переменные ссылаются на физически разные объекты, результат выражения А==В равен false. При выполнении инструкции А!=В все происходит аналогичным образом, но проверка выполняется на предмет неравенства - то есть результат выражения А!=В равен true, если переменные А и В ссылаются на разные объекты, а если они ссылаются на один и тот же объект, то результат выражения А!=В равен false. Если нас такое "поведение" операторов == и != не устраивает, то мы при описании класса можем эти операторы перегрузить. Особых ограничений (за исключением того, что операторы == и != перегружаются в паре) здесь нет, в том числе и касательно типа результата, возвращаемого соответствующими операторными методами. Исключительно простой пример перегрузки операторов сравнения представлен в примере ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr111_1 { class Program { // Класс без перегрузки операторов сравнения: class Alpha { // Целочисленное поле: public int code; // Конструктор с одним аргументом: public Alpha(int n) { // Присваивание значения полю: code = n; } } // Класс с перегрузкой операторов сравнения: class Bravo { // Целочисленное поле: public int code; // Конструктор с одним аргументом: public Bravo(int n) { // Присваивание значения полю: code = n; } // Перегрузка оператора "равно": public static bool operator==(Bravo a, Bravo b) { return a.code == b.code; } // Перегрузка оператора "не равно": public static bool operator!=(Bravo a, Bravo b) { return !(a == b); } } // Класс с главным методом: // Главный метод: static void Main() { // Создание объектов класса Alpha: Alpha A1 = new Alpha(100); Alpha A2 = new Alpha(100); Alpha A3 = new Alpha(200); // Проверка объектов на предмет // равенства или неравенства: Console.WriteLine("А1==А2 дает {0}", A1==A2); Console.WriteLine("А1!=А2 дает {0}", A1!=A2); Console.WriteLine("А1==АЗ дает {0}", A1==A3); Console.WriteLine("А1!=АЗ дает {0}", A1!=A3); // Создание объектов класса Bravo: Bravo B1 = new Bravo(100); Bravo B2 = new Bravo(100); Bravo B3 = new Bravo(200); // Проверка объектов на предмет // равенства или неравенства: Console.WriteLine("В1==В2 дает {0}", B1==B2); Console.WriteLine("В1!=В2 дает {0}", B1!=B2); Console.WriteLine("В1==ВЗ дает {0}", B1==B3); Console.WriteLine("В1!=ВЗ дает {0}", B1!=B3); // Задержка: Console.ReadLine(); } } }
В принципе, эта программа компилируется, но с предупреждением. Предупреждение - это не ошибка. Это скорее признак непоследовательного подхода к составлению программного кода. Суть предупреждения в данном случае в том, что в программе перегружены операторы == и !=, но не переопределены методы Equals() и GetHashCode(), наследуемые из класса Object. Методы обсудим в следующем шаге. Сначала проанализируем код программы и результат ее выполнения.
Рис.1. Результат выполнения программы
В программе описаны классы Alpha и Bravo. В каждом из классов есть целочисленное поле code и конструктор с одним аргументом. Принципиальное отличие между классами в том, что в классе Alpha операторы == и != не перегружаются, а в классе Bravo данные операторы перегружены. Перегружены они следующим образом. Операторный метод для оператора == описан с двумя аргументами класса Bravo, которые обозначены как a и b. Результатом метода возвращается значение выражения а.code==b. code.
return a.code == b.code;
В этом выражении используется оператор сравнения ==, но здесь он применяется по отношению к значениям целочисленного типа (поля code объявлены как целочисленные), и поэтому в данном случае действие оператора == предопределено характеристиками языка С#. Результат выражения а.code==b.code равен true, если значения полей code объектов а и b совпадают. Иначе значение выражения а.code==b.code равно false. Таким образом, результат операторного метода для оператора == равен true, если у сравниваемых объектов значения полей code совпадают (при этом сами объекты могут быть физически разными). Если поля code имеют разные значения, операторный метод для оператора == возвращает значение false.
Операторный метод для оператора сравнения != определен так: для аргументов а и b (объекты класса Bravo) результатом метода возвращается значение выражения !(a==b):
return !(a == b); .
Здесь вычисляется значение выражения a==b. Поскольку а и b - объекты класса Bravo, то для вычисления выражения a==b используется операторный метод для оператора ==. Результатом является логическое значение. Затем с помощью оператора логического отрицания ! результат выражения a==b заменяется на противоположный. Следовательно, оператор != работает по такой схеме. Если для двух указанных операндов оператор == возвращает значение true, то оператор != для тех же операндов возвращает значение false. И наоборот, если оператор == возвращает значение false, то оператор != с теми же операндами вернет значение true.
В главном методе программы создается по три объекта класса Alpha и Bravo. Эти объекты сравниваются с помощью операторов == и !=. Интерес представляет результат сравнения объектов А1 и А2 класса Alpha, с одной стороны, и объектов В1 и В2 класса Bravo - с другой. Примечательны объекты тем, что у них одинаковые значения полей. При этом результатом сравнения объектов А1 и А2 на предмет равенства (выражение А1==А2) является значение false, а при сравнении на предмет равенства объектов В1 и В2 получаем значение true. Причина в том, что при вычислении значения выражения А1==А2 (поскольку в классе Alpha операторы сравнения не перегружались) сравниваются ссылки на объекты (а они разные). А при вычислении значения выражения В1==В2, в соответствии с перегрузкой операторов сравнения в классе Bravo, сравниваются не ссылки, а значения полей объектов.
На следующем шаге мы закончим изучение этого вопроса.