C#. Перегрузка операторов сравнения

Перегрузка операторов сравнения ==, !=, <, >, <=, >=. Перегрузка логических побитовых операторов &, |, ^


Содержание


Поиск на других ресурсах:

1. Перегрузка бинарных операторов сравнения ==, !=. Пример

Операторы сравнения ==, != можно перегружать в классе. Особенность этих операторов состоит в том, что нужно реализовывать оба оператора одновременно.

Демонстрируется использование бинарных операторов сравнения для класса ArrayRange, реализующего массив целых чисел, находящихся в заданном диапазоне. В классе перегружаются операторы == и !=. Эти операторы производят поэлементное сравнение двух объектов типа ArrayRange.

Обязательна перегрузка этих операторов в паре. Если перегружается оператор ==, то нужно перегружать оператор !=.

using System;

namespace ConsoleApp6
{
  class ArrayRange
  {
    // Внутреннее поле - массив
    private int[] A;
    private int min; // минимальное значение
    private int max; // максимальное значение

    // Конструктор
    public ArrayRange(int[] _A, int _min, int _max)
    {
      // Установить границы
      if (_min <= _max)
      {
        min = _min; max = _max;
      }

      if (_min >= _max)
      {
        min = _max; max = _min;
      }

      // Сделать копию из исходного массива _A
      A = new int[_A.Length];
      for (int i = 0; i < A.Length; i++)
      {
        if (_A[i] < min)
          A[i] = min;
        else
        if (_A[i] > max)
          A[i] = max;
        else
          A[i] = _A[i];
      }
    }

    // Метод вывода внутреннего массива
    public void Print(string msg)
    {
      Console.WriteLine(msg);
      for (int i = 0; i < A.Length; i++)
        Console.Write(A[i] + " ");
      Console.WriteLine();
      Console.WriteLine("-----------------------------");
    }

    // Метод, возвращающий ссылку на массив A
    public int[] Get() { return A; }

    // Метод, который перегружает оператор ==
    public static bool operator==(ArrayRange A1, ArrayRange A2)
    {
      // 1. Сравнение расстояний
      if (A1.A.Length != A2.A.Length)
        return false;

      // 2. Поэлементное сравнение
      for (int i = 0; i < A1.A.Length; i++)
        if (A1.A[i] != A2.A[i])
          return false;

      return true;
    }

    // Метод, который перегружает оператор !=
    public static bool operator!=(ArrayRange A1, ArrayRange A2)
    {
      return !(A1 == A2);
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Создать массивы типа int[]
      int[] AI1 = { 2, 8, 1, -3, 4 };
      int[] AI2 = { 2, 8, 1, -3, 4 };

      // 2. Создать массивы типа ArrayRange
      ArrayRange A1 = new ArrayRange(AI1, 1, 5);
      ArrayRange A2 = new ArrayRange(AI2, 1, 5);

      // 3. Вывести массивы
      A1.Print("A1");
      A2.Print("A2");

      // 4. Вызвать перегруженный оператор сравнения массивов ==
      if (A1 == A2)
        Console.WriteLine("A1 == A2");
      else
        Console.WriteLine("A1 != A2");

      // 5. Вызвать другой перегруженный оператор !=
      if (A1 != A2)
        Console.WriteLine("A1 != A2");
      else
        Console.WriteLine("A1 == A2");

      Console.ReadKey();
    }
  }
}

Результат

A1
2 5 1 1 4
-----------------------------
A2
2 5 1 1 4
-----------------------------
A1 == A2
A1 == A2

 

2. Перегрузка бинарных операторов сравнения <, >, <=, >=. Пример

Перегрузка бинарных операторов сравнения >, <, >=, <= должна быть реализована попарно. Это означает, что обязательно нужно реализовывать следующие пары операторов:

  • > и <;
  • >= и <=.

На примере класса Line демонстрируется перегрузка бинарных операторов сравнения >, <, >=, <=. Методы класса

operator<()
operator>()
operator>=()
operator<=()

возвращают результат сравнения длин отрезков, являющихся входными параметрами.

using System;

namespace ConsoleApp6
{
  // Класс, описывающий линию, состоящую из двух точек
  class Line
  {
    // Внутренние переменные - координаты отрезка
    private double x1, y1, x2, y2;

    // Конструктор
    public Line(double x1, double y1, double x2, double y2)
    {
      this.x1 = x1;
      this.y1 = y1;
      this.x2 = x2;
      this.y2 = y2;
    }

    // Свойства доступа
    public double X1 { get { return x1; } }
    public double Y1 { get { return y1; } }
    public double X2 { get { return x2; } }
    public double Y2 { get { return y2; } }

    // Метод, выводящий координаты линии
    public void Print(string msg)
    {
      Console.Write(msg + "=> ( " + x1 + "; " + y1 + ") - ( " + x2 + "; " + y2 + "), ");
      Console.WriteLine(" length = {0:f2}", Length());
    }

    // Метод, возвращающий длину отрезка
    public double Length()
    {
      return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }

    // Перегруженные методы <, >, <=, >=.
    // В методах сравниваются длины отрезков ln1 и ln2
    public static bool operator>(Line ln1, Line ln2)
    {
      return ln1.Length() > ln2.Length();
    }

    public static bool operator<(Line ln1, Line ln2)
    {
      return !(ln1 > ln2);
    }

    public static bool operator>=(Line ln1, Line ln2)
    {
      return ln1.Length() >= ln2.Length();
    }

    public static bool operator <=(Line ln1, Line ln2)
    {
      return ln1.Length() <= ln2.Length();
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Объявить 2 отрезка
      Line ln1 = new Line(1, 1, 2, 4);
      Line ln2 = new Line(3, 5, -1, -7);

      // 2. Вывести информацию об отрезках ln1, ln2
      ln1.Print("ln1");
      ln2.Print("ln2");

      // 3. Сравнить ln1 < ln2
      if (ln1 < ln2)
        Console.WriteLine("ln1 < ln2");
      else
        Console.WriteLine("ln1 >= ln2");

      // 4. Сравнить ln1 > ln2
      if (ln1 > ln2)
        Console.WriteLine("ln1 > ln2");
      else
        Console.WriteLine("ln1 <= ln2");

      // 5. Сравнить ln1 <= ln2
      if (ln1 <= ln2)
        Console.WriteLine("ln1 <= ln2");
      else
        Console.WriteLine("ln1 > ln2");

      // 6. Сравнить ln1 >= ln2
      if (ln1 >= ln2)
        Console.WriteLine("ln1 >= ln2");
      else
        Console.WriteLine("ln1 < ln2");

      Console.ReadKey();
    }
  }
}

Результат

ln1=> ( 1; 1) - ( 2; 4), length = 3.16
ln2=> ( 3; 5) - ( -1; -7), length = 12.65
ln1 < ln2
ln1 <= ln2
ln1 <= ln2
ln1 < ln2

 

3. Перегрузка битовых бинарных операторов & (логическое побитовое «И»), | (логическое побитовое «ИЛИ»), ^ (сложение по модулю 2)

В примере показано поэлементное оперирование массивами, состоящими из 0 и 1. Задан класс ArrayBits, в котором объявляется массив типа byte[]. Элементы массива состоят из значений 0 и 1. В классе перегружаются логические побитовые операторы &, |, ^.

Методы, которые перегружают операторы реализуют соответствующие операции над каждым элементом массива. Операторный метод operator&() выполняет побитовое логическое «И» над элементами двух массивов. Операторный метод operator|() выполняет побитовое логическое «ИЛИ» над элементами массивов. Соответственно метод operator^() выполняет побитовое логическое сложение над каждым из элементов массива.

Демонстрационный программный код консольного приложения следующий.

using System;

namespace ConsoleApp6
{
  // Класс, описывающий массив битов 0, 1
  class ArrayBits
  {
    private byte[] AB;

    // Конструктор
    public ArrayBits(byte[] _AB)
    {
      // Создать новый массив, в котором ненулевые значения равны 1,
      // а нулевые значения равны 0
      AB = new byte[_AB.Length];

      for (int i = 0; i < _AB.Length; i++)
        if (_AB[i] != 0)
          AB[i] = 1;
        else
          AB[i] = 0;
    }

    // Метод, который выводит внутренний массив AB
    public void Print(string msg)
    {
      Console.Write(msg + " => ");
      for (int i = 0; i < AB.Length; i++)
        Console.Write(AB[i] + " ");
      Console.WriteLine();
    }

    // Перегрузка операторов &, |, ^
    // Побитовое "И"
    public static ArrayBits operator&(ArrayBits AB1, ArrayBits AB2)
    {
      ArrayBits res;

      // Выбрать массив с наименьшей длиной
      // и на его основе создать результирующий массив
      if (AB1.AB.Length<AB2.AB.Length)
      {
        res = new ArrayBits(AB1.AB);
      }
      else
      {
        res = new ArrayBits(AB2.AB);
      }

      // Реализовать поэлементное побитовое "И",
      // сформировать массив res
      for (int i = 0; i < res.AB.Length; i++)
        if ((AB1.AB[i] == 1) && (AB2.AB[i] == 1))
          res.AB[i] = 1;
        else
          res.AB[i] = 0;

      // Вернуть результирующий массив
      return res;
    }

    // Побитовое "ИЛИ"
    public static ArrayBits operator|(ArrayBits AB1, ArrayBits AB2)
    {
      ArrayBits res;

      // Выбрать массив с наименьшей длиной
      // и на его основе создать результирующий массив
      if (AB1.AB.Length < AB2.AB.Length)
      {
        res = new ArrayBits(AB1.AB);
      }
      else
      {
        res = new ArrayBits(AB2.AB);
      }

      // Реализовать поэлементное побитовое "ИЛИ",
      // сформировать массив res
      for (int i = 0; i < res.AB.Length; i++)
        if ((AB1.AB[i] == 0) && (AB2.AB[i] == 0))
          res.AB[i] = 0;
        else
          res.AB[i] = 1;

      // Вернуть результирующий массив
      return res;
    }

    // Побитовое сложение по модулю 2
    public static ArrayBits operator^(ArrayBits AB1, ArrayBits AB2)
    {
      ArrayBits res;

      // Выбрать массив с наименьшей длиной
      // и на его основе создать результирующий массив
      if (AB1.AB.Length < AB2.AB.Length)
      {
        res = new ArrayBits(AB1.AB);
      }
      else
      {
        res = new ArrayBits(AB2.AB);
      }

      // Реализовать поэлементное побитовое сложение по модулю 2,
      // сформировать массив res
      for (int i = 0; i < res.AB.Length; i++)
        if (AB1.AB[i] == AB2.AB[i])
          res.AB[i] = 0;
        else
          res.AB[i] = 1;

      // Вернуть результирующий массив
      return res;
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Создать 2 тестируемых массива типа byte[]
      byte[] A1 = { 2, 3, 0, 0, 4, 7, 0, 55 };
      byte[] A2 = { 1, 0, 6, 0, 2, 0, 8, 9, 4, 0, 0 };

      // 2. Создать 2 объекта на основе массивов A1 и A2
      ArrayBits AB1 = new ArrayBits(A1);
      ArrayBits AB2 = new ArrayBits(A2);

      // 3. Вывести массивы в экземплярах для контроля
      AB1.Print("AB1 ");
      AB2.Print("AB2 ");

      // 4. Реализовать побитовое & - И
      ArrayBits AB3;
      AB3 = AB1 & AB2; // => ArrayBits.operator&(AB1, AB2);
      AB3.Print("AB1 & AB2");

      // 5. Вывести результат побитового | - ИЛИ
      (AB1 | AB2).Print("AB1 | AB2"); // => ArrayBits.operator|(AB1, AB2);

      // 6. Реализовать побитовое ^ - сложение по модулю 2
      ArrayBits AB4 = AB1 ^ AB2; // => ArrayBits.operator^(AB1, AB2);
      AB4.Print("AB1 ^ AB2");

      Console.ReadKey();
    }
  }
}

Результат

AB1 => 1 1 0 0 1 1 0 1
AB2 => 1 0 1 0 1 0 1 1 1 0 0
AB1 & AB2 => 1 0 0 0 1 0 0 1
AB1 | AB2 => 1 1 1 0 1 1 1 1
AB1 ^ AB2 => 0 1 1 0 0 1 1 0

 

4. Перегрузка операторов сдвига <<, >>. Пример

В классе операторы сдвига перегружаются в паре. Это означает, что если перегружается оператор << (сдвиг влево), то также нужно перегружать оператор >> (сдвиг вправо).

using System;

namespace ConsoleApp6
{
  // Массив целых чисел
  class ArrayInt
  {
    private int[] AI;

    // Конструктор
    public ArrayInt(int[] _AD)
    {
      // Копирование AD = _AD
      AI = new int[_AD.Length];
      for (int i = 0; i < AI.Length; i++)
        AI[i] = _AD[i];
    }

    // Метод, который выводит массив AD
    public void Print(string msg)
    {
      Console.Write(msg + " => ");
      for (int i = 0; i < AI.Length; i++)
        Console.Write(AI[i] + " ");
      Console.WriteLine();
    }

    // Перегрузка оператора сдвига влево <<
    // Элементи массива сдвигаются циклически влево,
    // первый элемент массива становится на место последнего.
    // Обратить внимание: метод возвращает экземпляр типа ArrayInt и целое число int
    public static ArrayInt operator<<(ArrayInt AI, int n)
    {
      // 1. Создать экземпляр массива AI
      ArrayInt res = new ArrayInt(AI.AI);

      for (int k = 0; k < n; k++)
      {
        // Циклический сдвиг на одну позицию влево
        int t = res.AI[0];
        for (int i = 0; i < res.AI.Length - 1; i++)
          res.AI[i] = res.AI[i + 1];
        res.AI[AI.AI.Length - 1] = t;
      }

      // 2. Результирующий экземпляр
      return res;
    }

    // Операторный метод, перегружающий оператор >> сдвига вправо
    public static ArrayInt operator >>(ArrayInt AI, int n)
    {
      // 1. Создать экземпляр массива AI
      ArrayInt res = new ArrayInt(AI.AI);

      for (int k = 0; k < n; k++)
      {
        // Циклический сдвиг на одну позицию влево
        int t = res.AI[AI.AI.Length - 1];
        for (int i = res.AI.Length - 1; i >= 1; i--)
          res.AI[i] = res.AI[i - 1];
        res.AI[0] = t;
      }

      // 2. Результирующий экземпляр
      return res;
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Объявить массив типа int[]
      int[] A = { 2, 8, 9, 15, 27 };

      // 2. Создать экземпляр типа ArrayInt
      ArrayInt AI = new ArrayInt(A);
      AI.Print("AI "); // Вывести массив

      // 3. Вызвать оператор сдвига влево на 2 позиции
      ArrayInt res = AI << 2;
      res.Print("AI << 2");

      // 4. Вызвать оператор сдвига вправо на 4 позиции
      res = AI >> 4;
      res.Print("AI >> 4");

      Console.ReadKey();
    }
  }
}

Результат

AI => 2 8 9 15 27
AI << 2 => 9 15 27 2 8
AI >> 4 => 8 9 15 27 2

 


Связанные темы