C#. Перегрузка бинарных операторов

Перегрузка бинарных операторов. Перегрузка операторов +, , *, /, %. Вложение произвольной логики при перегрузке

Перед изучением данной темы рекомендуется ознакомиться со следующей темой:


Содержание


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

1. Особенности перегрузки бинарных операторов

В языке C# допускается перегружать следующие бинарные операторы:

  • + – сложение (a + b);
  • – вычитание (a — b);
  • * – умножение (a * b);
  • / – деление (a / b);
  • % – оператор взятия остатка от деления. Может быть перегружен по собственному усмотрению;
  • & – побитовое логическое «И»;
  • | – побитовое логическое «ИЛИ»;
  • ^ – побитовое сложение по модулю 2;
  • <, > – операторы сравнения (меньше, больше). В классе эти операторы обязательно перегружаются парой (оба);
  • <=, >= – операторы сравнения (меньше или равно, больше или равно). Эти операторы перегружаются парой;
  • ==, != – операторы сравнения (равно, не равно). Перегружаются парой в классе.

При перегрузке бинарных операторов создается статический метод, получающий 2 параметра. Тип этих параметров является типом класса, в котором реализуется метод, перегружающий оператор.

В наиболее общем случае перегрузка бинарного оператора в классе с именем ClassName имеет вид:

class ClassName
{
  public static ClassName operator op(ClassName obj1, ClassName obj2)
  {
    // Действия, которые нужно выполнить при вызове obj1 op obj2
    // ...
  }
}

здесь

  • op – один из стандартных бинарных операторов, которые можно перегружать (+, -, ==, > и т.д.);
  • obj1 – экземпляр, находящийся слева от оператора op;
  • obj2 – экземпляр, находящийся справа от оператора op.

После перегрузки экземпляры obj1 и obj2 можно сочетать оператором op в более наглядном виде

obj1 op obj2

К примеру, если перегрузить оператор +, то вызов соответствующего метода будет

obj1 + obj2

 

2. Перегрузка бинарных операторов +, , *, /. Пример

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

В классе Complex переопределены операторы +, -, *, / реализующие соответственно сложение, вычитание, умножение и деление двух комплексных чисел.

using System;

namespace ConsoleApp6
{
  class Complex
  {
    // Внутренние поля класса
    private double re, im;

    // Конструктор
    public Complex(double re, double im)
    {
      this.re = re;
      this.im = im;
    }

    // Свойства доступа к внутренним полям
    public double Re
    {
      get { return re; }
      set { re = value; }
    }

    public double Im
    {
      get { return im; }
      set { im = value; }
    }

    // Операторный метод, который перегружает бинарный оператор '+',
    // реализует сложение комплексных чисел
    public static Complex operator+(Complex cm1, Complex cm2)
    {
      return new Complex(cm1.re + cm2.re, cm1.im + cm2.im);
    }

    // Операторный метод, перегружающий бинарный оператор '-',
    // реализует вычитание комплексных чисел
    public static Complex operator-(Complex cm1, Complex cm2)
    {
      return new Complex(cm1.re - cm2.re, cm1.im - cm2.im);
    }

    // Метод, перегружающий оператор '*'
    // реализует умножение комплексных чисел
    public static Complex operator*(Complex cm1, Complex cm2)
    {
      return new Complex(cm1.re * cm2.re - cm1.im * cm2.im, cm1.re * cm2.im + cm1.im * cm2.re);
    }
 
    // Перегрузка оператора '/' - деление комплексных чисел
    public static Complex operator/(Complex cm1, Complex cm2)
    {
      double denom = cm2.re * cm2.re + cm2.im * cm2.im;
      return new Complex((cm1.re * cm2.re + cm1.im * cm2.im) / denom,
        (cm1.im * cm2.re - cm1.re * cm2.im) / denom);
    }

    // Метод, выводящий состояние текущего экземпляра
    public void Print(string msg)
    {
      Console.Write(msg + " => ");
      Console.Write(re);
      if (im >= 0)
        Console.Write("+");
      Console.WriteLine(im.ToString() + "*j");
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Создать 2 объекта типа Complex
      Complex cm1 = new Complex(3, 5);
      Complex cm2 = new Complex(2, -4);

      // 2. Вывести объекты
      cm1.Print("cm1");
      cm2.Print("cm2");

      // 3. Сложить объекты
      Complex cm3;
      cm3 = cm1 + cm2; // вызывается метод, который перегружает оператор '+'
      cm3.Print("cm1 + cm2");

      // 4. Отнять - вызов метода Complex.operator-()
      (cm1 - cm2).Print("cm1-cm2");

      // 5. Умножить
      cm3 = cm1 * cm2;
      cm3.Print("cm1 * cm2");

      // 6. Разделить
      cm3 = cm1 / cm2;
      cm3.Print("cm1 / cm2");

      // 7. Использование операторов +=, -=, *=, /=
      Complex cm4 = new Complex(1, 1);
      cm4 += cm1; // => cm4 = cm4 + cm1;
      cm4.Print("cm4");

      Complex cm5 = new Complex(2, 3);
      cm5 *= cm2; // => cm5 = cm5 * cm2
      cm5.Print("cm5");

      Console.ReadKey();
    }
  }
}

Результат

cm1 => 3+5*j
cm2 => 2-4*j
cm1 + cm2 => 5+1*j
cm1-cm2 => 1+9*j
cm1 * cm2 => 26-2*j
cm1 / cm2 => -0.7+1.1*j
cm4 => 4+6*j
cm5 => 16-2*j

 

3. Особенности перегрузки операторов +=, -=, *=, /=, %=

Сокращенные операторы присвоения +=, -=, *=, /=, %= и другие подобные считаются перегруженными, как только перегружены соответствующие им операторы +, -, *, /, % и другие.

На примере класса Point перегружаются операторы + и -. Затем показывается вызов соответствующих операторов +=, –= для экземпляров класса Point.

Перегруженные операторы + и – выполняют сложение и вычитание координат точек.

using System;

namespace ConsoleApp6
{
  // Класс, описывающий точку на координатной плоскости
  class Point
  {
    // Внутренние переменные
    private int x, y;

    // Конструктор
    public Point(int x, int y)
    {
      this.x = x;
      this.y = y;
    }

    // Свойства доступа к координатам
    public int X
    {
      get { return x; }
    }

    public int Y
    {
      get { return y; }
    }

    // Метод, выводящий текущие значения x, y
    public void Print(string msg)
    {
      Console.WriteLine(msg + " => " + "( " + x + "; " + y + ")");
    }

    // Перегруженные операторы '+' та '-'
    public static Point operator+(Point p1, Point p2)
    {
      return new Point(p1.x + p2.x, p1.y + p2.y);
    }

    public static Point operator-(Point p1, Point p2)
    {
      return new Point(p1.x - p2.x, p1.y - p2.y);
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Создать 2 экземпляра класса Point
      Point pt1 = new Point(3, 8);
      Point pt2 = new Point(4, 9);

      // 2. Использовать оператор += для вычисления суммы точек pt1 и pt2
      pt1 += pt2; // =>   pt1 = pt1 + pt2; - выполняется: pt1 = Point.operator+(pt1, pt2)
      pt1.Print("pt1");

      // 3. Вызвать оператор -= для вычисления разности pt2 = pt2 - pt1;
      pt2 -= pt1; // pt2 = Point.operator(pt2, pt1);
      pt2.Print("pt2");

      Console.ReadKey();
    }
  }
}

После перегрузки операторов + и — в классе Point в функции main() вызываются соответствующие операторы += и -= в строках

pt1 += pt2;
pt2 -= pt1;

После выполнения программа выдает следующий результат

pt1 => ( 7; 17)
pt2 => ( -3; -8)

 

4. Пример перегрузки оператора %. Определение среднего арифметического двух массивов. Программирование произвольной логики при перегрузке операторов

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

Оператор % определяет остаток от деления целочисленных значений. Однако в нашем случае реализована другая логика. Определяется среднее арифметическое двух операндов, являющихся массивами типа ArrayDouble. Этот пример носит демонстрационный характер и показывает как по-разному можно программировать логику операторных методов.

Следует обратить внимание, что операторный метод operator%() возвращает тип double, а не ArrayDouble.

using System;

namespace ConsoleApp6
{
  // Массив чисел с плавающей запятой
  class ArrayDouble
  {
    private double[] AD;

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

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

    // Перегрузка оператора %.
    // Определение среднего арифметического двух массивов-операндов.
    // Обратить внимание: метод возвращает double а не ArrayDouble
    public static double operator%(ArrayDouble AD1, ArrayDouble AD2)
    {
      // 1. Вычислить сумму элементов массивов
      double res = 0;
      for (int i = 0; i < AD1.AD.Length; i++)
        res += AD1.AD[i];
      for (int i = 0; i < AD2.AD.Length; i++)
        res += AD2.AD[i];

      // 2. Вернуть среднее арифметическое
      return res / (AD1.AD.Length + AD2.AD.Length);
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Создать 2 массива типа double[]
      double[] A1 = { 2.4, 3.1, 0.8, -2.2, 0.9, 11.6 };
      double[] A2 = { 3.8, 11.7, -4.4, 0.8 };

      // 2. На основе массивов создать 2 экземпляра
      ArrayDouble AD1 = new ArrayDouble(A1);
      ArrayDouble AD2 = new ArrayDouble(A2);

      // 3. Вывести массивы для контроля
      AD1.Print("AD1");
      AD2.Print("AD2");

      // 4. Использовать перегруженный оператор % для
      //    вычисления среднего арифметического
      double average = AD1 % AD2;
      Console.WriteLine("AD1 % AD2 = {0:f3}", average);

      Console.ReadKey();
    }
  }
}

Результат

AD1 => 2.4 3.1 0.8 -2.2 0.9 11.6
AD2 => 3.8 11.7 -4.4 0.8
AD1 % AD2 = 2.850

 


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