Шаг 111.
Язык программирования C#. Начала
Перегрузка операторов. Перегрузка операторов сравнения (еще продолжение)

    На этом шаге мы рассмотрим перегрузку операторов == и !=.

    Похожим образом обстоят дела с перегрузкой операторов "равно" == и "не равно" !=. Правда, есть и небольшие особенности. Чтобы понять их суть, необходимо сделать небольшое отступление.

    Даже если мы не перегружаем операторы == и != объектов класса, мы все равно можем использовать эти операторы (в отличие от операторов <, >, <= и >=) для сравнения объектов. Например, если объекты А и В относятся к одному и тому же классу, то операции вида А==В и А!=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, сравниваются не ссылки, а значения полей объектов.

    На следующем шаге мы закончим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг