C#. Перевантаження операторів. Загальні відомості. Перевантаження унарних операторів

Перевантаження операторів. Загальні відомості. Перевантаження унарних операторів , !, ++,


Зміст


Пошук на інших ресурсах:

1. Поняття про перевантаження операторів

У мові C# існує можливість задавати оператор (+, –, != та інше) для конкретного класу чи структури, який може бути використаний для виконання якихось дій. Як правило, ці дії виконуються над об’єктами цього класу (структури). Представлення дій у вигляді зрозумілих (природніх) операторів покращує сприйняття програмного коду. Таким чином, використання відомих операторів може бути як для базових типів (int, double, char та інших) так і для користувацьких класів (користувацьких типів).

Механізм задавання стандартних операторів для заданого класу чи структури з метою покращення читабельності програм називається перевантаженням операторів. Оператори можна перевантажувати як для класів так і для структур.

Багато стандартних класів реалізують перевантаження багатьох операторів. Наприклад, клас String має перевантажені оператори == та !=, що дає змогу зручно порівнювати два рядки на рівність (нерівність).

 

2. Вимоги та обмеження, що накладаються при перевантаженні операторів

При виконанні перевантаження операторів у класах потрібно дотримуватись ряду вимог:

  • для реалізації перевантеження потрібно використовувати загальнодоступні статичні методи, які оголошуються в класі;
  • в операторному методі забороняється використовувати модифікатори ref або out;
  • в операторному методі тип параметру або тип значення, що повертається, повинен співпадати з типом (класом), в якому цей оператор перевантажується.

Також на перевантаження операторів накладаються наступні обмеження:

  • перевантаження не змінює пріоритет операторів;
  • при перевантаженні оператору кількість операндів, які використовуються, змінити неможливо;
  • не всі оператори можна перевантажувати.

 

3. Перелік операторів, які допускають та не допускають перевантаження

Не всі оператори допускають перевантаження. У таблиці нижче подано оператори, які можна перевантажувати.

 

Оператори Категорія операторів
Зміна знаку на протилежний
! Логічне заперечення
~ Доповнення до 1
++, Інкремент, декремент
true, false Істинність об’єкту
+, , *, /, % Арифметичні оператори
&&, || Логічні оператори
&, |, ^, <<, >> Бітові оператори
==, !=, <, >, <=, >= Оператори порівняння
[] Операція доступу до елементів масиву
() Операції перетворення

Нижче наведено перелік операторів, які не допускають перевантаження.

Оператори Категорія операторів
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= Скорочені оператори присвоєння
= Присвоєння
. Доступ до елементів
?: Тернарний умовний оператор
new Створення об’єкту
as, is, typeof Отримання інформації про тип
->, sizeof, *, & Доступні в небезпечному коді

 

4. Загальна форма перевантаження оператора в класі. Ключове слово operator

Для перевантаження оператора в класі використовується ключове слово operator за наступним синтаксисом

public static return_type operator op(parameters)

тут

  • op – оператор, який перевантажується (+, –, != тощо);
  • return_type – тип, що повертається при використанні оператора op;
  • parameters – список параметрів, які отримує оператор. Для унарних операторів (-, !, ++, –) parameters містить тільки 1 параметр, для бінарних – 2 параметри.

 

5. Особливості перевантаження унарних операторів

До унарних операторів, які можна перевантажувати, відносяться оператори:

  • – унарний мінус;
  • ! – логічне заперечення;
  • ++ – інкремент;
  •  – декремент.

Оскільки, всі перевантажені оператори є статичними методами, то вони не отримують покажчик this поточного екземпляру. Як наслідок, всі операторні методи що перевантажують унарні оператори, отримують один параметр. Цей параметр повинен бути типу класу.

У найбільш загальному випадку перевантаження унарного оператора в класі ClassName виглядає наступним чином

class ClassName
{
  ...

  public static return_type op(ClassName param)
  {
    // Дії, які потрібно виконати
    // ...
  }
}

тут

  • op – позначення одного з унарних операторів –, !, ++, –;
  • param – деякий параметр;
  • return_type – тип, який повертається. Це може бути будь-який тип, що використовується у програмі.

Після цього, оператор op можна використовувати для класу ClassName приблизно наступним чином

value = op Obj;

тут

  • value – значення типу return_type, що отримується;
  • Obj – екземпляр класу ClassName.

Наприклад, якщо перевантажено оператор ++, то виклик буде

value = ++Obj;

 

6. Приклад перевантаження унарного оператора (мінус)

У прикладі приводиться перевантаження унарного оператора – (мінус) на прикладі класу, що описує комплексне число. З допомогою перевантаженого оператора знаки дійсної та уявної частини комплексного числа змінюються на протилежні.

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 cm)
    {
      return new Complex(-cm.re, -cm.im);
    }

    // Метод, що виводить стан поточного екземпляру
    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)
    {
      // Створити екземпляр класу Complex
      Complex c1 = new Complex(2.4, -3.8);

      // Вивести стан екземпляру
      c1.Print("c1"); // c1 => 2.4-3.8*j

      // Створити посилання на Complex
      Complex c2;

      // Присвоїти посиланню результат повернення
      // з перевантаженого оператора -
      c2 = -c1; // викликається метод Complex::operator-(c1);

      // Вивести стан екземпляру с2
      c2.Print("c2"); // c2 => -2.4+3.8*j

      Console.ReadKey();
    }
  }
}

Результат

c1 => 2.4-3.8*j
c2 => -2.4+3.8*j

 

7. Приклад перевантаження унарного оператора ! (заперечення)

Реалізовано перевантаження унарного оператора ! на прикладі класу, що описує трикутник за його сторонами. Операторний метод повертає значення true, якщо трикутник є рівностороннім з заданою точністю. В іншому випадку повертається false.

У прикладі демонструється тільки мінімальний набір методів з метою демонстрації перевантаження оператора ! (заперечення).

using System;

namespace ConsoleApp6
{
  class Triangle
  {
    // Внутрішні поля класу - координати крайніх точок трикутника
    private double x1, y1, x2, y2, x3, y3;

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

    // Операторний метод, що перевантажує унарний оператор !
    // Метод повертає true, якщо довжини сторін є рівні
    // з точністю 5 знаків після коми.
    public static bool operator!(Triangle tr)
    {
      // Точність - 5 знаків після коми
      double eps = 1E-5;

      // Відстань між точками 1 - 2
      double a = Math.Sqrt((tr.x1 - tr.x2) * (tr.x1 - tr.x2) +
        (tr.y1 - tr.y2) * (tr.y1 - tr.y2));

      // Відстань між точками 1 - 3
      double b = Math.Sqrt((tr.x1 - tr.x3) * (tr.x1 - tr.x3) +
        (tr.y1 - tr.y3) * (tr.y1 - tr.y3));

      // Відстань між точками 2 - 3
      double c = Math.Sqrt((tr.x2 - tr.x3) * (tr.x2 - tr.x3) +
        (tr.y2 - tr.y3) * (tr.y2 - tr.y3));

      return (Math.Abs(a - b) < eps) &&
        (Math.Abs(b - c) < eps) &&
        (Math.Abs(a - c) < eps);
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // Створити екземпляр класу Triangle
      Triangle tr = new Triangle(1, 1, 2, 2, 3, 1);

      // Викликати перевантажений оператор Triangle::operator!()
      if (!tr)
        Console.WriteLine("The sides of the triangle are equal");
      else
        Console.WriteLine("The sides of the triangle are not equal");

      Console.ReadKey();
    }
  }
}

Результат

The sides of the triangle are not equal

 

8. Приклад перевантаження унарних операторів ++, (інкремент, декремент)

Унарні оператори інкременту (++) та декременту () також можуть бути перевантажені. У C# немає відмінності при перевантаженні префіксної чи постфіксної форми операторів інкременту та декременту. Перевантажується префіксна форма, яка має таку саму дію як постфіксна форма.

У прикладі реалізується клас Point, що описує точку на координатній площині. З метою демонстрації, у класі перевантажуються операторні функції

public static Point operator++(Point pt);
public static Point operator--(Point pt);

У цих функціях значення координат x, y відповідно збільшуються та зменшуються на 1.

using System;

namespace ConsoleApp6
{
  class Point
  {
    // Внутрішні поля - координати точки
    private double x, y;

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

    // Властивості доступу до внутрішніх полів
    public double X
    {
      get { return x; }
      set { x = value; }
    }

    public double Y
    {
      get { return y; }
      set { y = value; }
    }

    // Метод, що виводить внутрішній стан класу
    public void Print(string msg)
    {
      Console.WriteLine(msg + " => ( " + x + "; " + y + ")");
    }

    // Методи, що перевантажують оператори інкременту (++) та декременту (--)
    public static Point operator++(Point pt)
    {
      return new Point(pt.x + 1, pt.y + 1);
    }

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

  internal class Program
  {
    static void Main(string[] args)
    {
      // 1. Оголосити екземпляр класу Point
      Point pt1 = new Point(2, 8);

      // 2. Викликати постфіксну форму оператора інкременту ++
      pt1++; // викликається перевантажений оператор ++ для класу Point

      // 3. Вивести стан екземпляру pt1
      pt1.Print("pt1"); // pt1 => ( 3; 9)

      // 4. Викликати префіксну форму оператора декременту --
      --pt1;

      // 5. Вивести стан екземпляру pt1 повторно
      pt1.Print("pt1"); // pt1 => ( 2; 8)

      Console.ReadKey();
    }
  }
}

Результат

pt1 => ( 3; 9)
pt1 => ( 2; 8)

 

9. Приклад перевантаження унарного оператора ~ (доповнення до 1)

У прикладі демонструється перевантаження унарного оператора ~ (тільда).
Оголошується клас Integer, що описує ціле невід’ємне число. У класі оголошується операторний метод

public static Integer operator~(Integer num)

який перевантажує оператор ~. Метод повертає екземпляр класу Integer, в якому число є реверсоване (читається у зворотному порядку).

using System;

namespace ConsoleApp6
{
  // Клас, що описує цілочисельне значення,
  // яке може бути тільки додатнім і має додаткові властивості
  class Integer
  {
    // Внутрішнє поле - число
    private int value;

    // Конструктор
    public Integer(int value)
    {
      if (value < 0)
        this.value = -value;
      else
        this.value = value;
    }

    // Властивості доступу
    public int Value
    {
      get { return value; }
      set {
        if (value < 0)
          this.value = -value;
        else
          this.value = value;
      }
    }

    // Метод перевантаження оператора ~
    // Метод інвертує число. Наприклад, 2351 => 1532
    public static Integer operator~(Integer num)
    {
      int result = 0;
      int t;
      int pow;

      // Визначити порядок числа
      t = num.Value / 10;
      pow = 1;
      while (t > 0)
      {
        t = t / 10;
        pow *= 10;
      }

      // Сформувати результуюче число
      t = num.Value;
      while (t>0)
      {
        result = result + (t % 10) * pow;
        t = t / 10;
        pow = pow / 10;
      }

      return new Integer(result);
    }

    // Метод, що виводить внутрішнє поле
    public void Print(string msg)
    {
      Console.WriteLine(msg + " => " + value);
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      // Оголосити екземпляр класу Integer
      Integer num1 = new Integer(25631);

      // Вивести значення екземпляру
      num1.Print("num1");

      // Викликати метод, що перевантажує оператор ~
      Integer num2 = ~num1;

      // Вивести результат
      num2.Print("num2");

      Console.ReadKey();
    }
  }
}

Результат

num1 => 25631
num2 => 13652

 


Споріднені теми