C#: Перегрузка стандартных операторов или «Новая жизнь для +, -, *, /»

Сегодня мы поговорим такой немаловажной вещи языка C#, да и всего ООП в целом — перегрузка операторов. Возможность делать такую перегрузку позволяет задавать свои правила работы для таких операторов как +, -, /, ++, —. Что свою очередь позволяет выполнять данные операции для свежеиспеченных классов. Не будем тянуть кота за одно место, начнем наше программирование…

Создадим проект (неважно консольный или винформ). Я создам винформ. Перейдем в файл Form1.cs. Первое, что нам необходимо сделать — создать какой-либо свой класс. Остановимся на классе, который будет отвечать за дробные числа в привычном для любого школьника виде: числитель и знаменатель.

public class Drob
{
    //Дроби вида 'x / y'
    int x = 1;
    int y = 1;

    public Drob(int x, int y)
    {
       this.x = x;
       this.y = y;
    }
}

Думаю код выше не должен вызвать вопросы. Там все понятно даже самому начинающему программисту, знакомому с принципами ООП.

Теперь добавим метод для отображения нашей дроби в MessageBox.

public class Drob
{
    //Дроби вида 'x / y'
    int x = 1;
    int y = 1;

    public Drob(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override string ToString()
    {
      return x.ToString() + "/" + y.ToString();
    }
}

Перейдем в событие формы Load и выполним следующий код:

Drob A = new Drob(1, 5);
MessageBox.Show(A.ToString());

Запустим наш проект. Мы увидим как на экране появилась дробь «1/5». Интересно, не правда ли? Однако смысла наш класс пока не несет. Было бы не плохо если бы мы смогли выполнить код:

Drob A = new Drob(1, 5);
Drob B = new Drob(1, 5);
MessageBox.Show((A+B).ToString());

и увидеть на мониторе сообщение «2/5». Однако, если мы сейчас напишем такой код, то ничего кроме ошибок не увидим. Давайте исправим данную недоработку. Перегрузим наши операторы. Первым будет оператор +.

public class Drob
{
    //Дроби вида 'x / y'
    int x = 1;
    int y = 1;

    public Drob(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override string ToString()
    {
      return x.ToString() + "/" + y.ToString();
    }

    public static Drob operator+(Drob a, Drob b)
    {
       if (a.y == b.y)
          return new Drob(a.x + b.x, a.y);
       else
       {
          //Общий знаменатель
          int z = 0, newXa = 0, newXb = 0;
          if (b.y % a.y == 0)
          {
             z = b.y;
             newXb = b.x;
             newXa = a.x * (b.y / a.y);
           }
           else if (a.y % b.y == 0)
           {
              z = a.y;
              newXb = b.x * (a.y / b.y);
              newXa = a.x;
           }
           else
           {
             z = a.y * b.y;
             newXa = a.x * b.y;
             newXb = b.x * a.y;
           }
           return new Drob(newXa + newXb, z);
       }
    }
}

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

Не трудно заметить, что прегруженный оператор по сути является функцией одной или двух переменных, которая возвращает объект созданного класса. Давайте теперь попробуем запустить наш проект. Магия сработала! Программа показала нам дробь «2/5».
Давайте рассмотрим перегрузку унарного оператора ++.

public class Drob
{
    //Дроби вида 'x / y'
    int x = 1;
    int y = 1;

    public Drob(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override string ToString()
    {
      return x.ToString() + "/" + y.ToString();
    }

    public static Drob operator+(Drob a, Drob b)
    {
       if (a.y == b.y)
          return new Drob(a.x + b.x, a.y);
       else
       {
          //Общий знаменатель
          int z = 0, newXa = 0, newXb = 0;
          if (b.y % a.y == 0)
          {
             z = b.y;
             newXb = b.x;
             newXa = a.x * (b.y / a.y);
           }
           else if (a.y % b.y == 0)
           {
              z = a.y;
              newXb = b.x * (a.y / b.y);
              newXa = a.x;
           }
           else
           {
             z = a.y * b.y;
             newXa = a.x * b.y;
             newXb = b.x * a.y;
           }
           return new Drob(newXa + newXb, z);
       }
    }

    public static Drob operator ++(Drob a)
    {
      return a + (new Drob(a.y, a.y));
    }

}

Хитро? А то!) Зачем мучатся и писать алгоритм сложения на единицу, если у нас уже реализован алгоритм сложения на любую дробь? Ведь единица в нашем случае — это дробь с числителем равным знаменателю и равным знаменателю нашей дроби. Выполним код:

Drob A = new Drob(1, 5);
A++;
MessageBox.Show(A.ToString());

На экран вывелось сообщение с дробью «6/5». Все верно. Думаю смысл понятен и перегрузить оператор умножения и деления Вы сможете сами. Если нет, то пишите в комментариях и я продолжу данную статью. Спасибо за внимание!

Запись опубликована в рубрике C# с метками , , , , , , . Добавьте в закладки постоянную ссылку.