Введення в перевантаження операторів у C# - все про IT та програмування

Written on 07 Квітня 2012 . Posted in C#.NET

Перевантаження операторів - це потужна і недостатньо використовується (але часто неправильно використовується) можливість, здатна зробити код простішим, а використання об'єктів - інтуїтивно-зрозумілим. Додавання кількох простих перевантажених операторів у клас чи структуру дасть вам можливість:

  1. виконувати перетворення в/з вашого типу (і) на інші типи
  2. здійснювати математичні/логічні операції над вашим типом і над ним самим або іншими типами

Оператори перетворення

Існують кілька способів перетворення між типами, але серед них найбільш поширеним є використання неявних/явних операторів перетворення.

Неявне перетворення

Неявне перетворення означає, що можна безпосередньо надавати екземпляр одного типу іншому.

Давайте розглянемо приклад.

Щоб явно перетворити структуру назад на int, необхідно додати ще один оператор перетворення:

Тепер можна надавати myIntStructInstance безпосередньо int (тип цілих чисел).

Також можна викликати будь-яку іншу функцію, яка прийматиме int, і передати їй екземпляр структури безпосередньо. Це можна зробити з будь-яким потрібним числом типів.

Структура може мати рядкове поле, і в цьому випадку було б корисно мати можливість створити її екземпляр шляхом безпосереднього присвоєння рядка. Ця структура може виглядати так:

. та екземпляр структури може бути створений шляхом присвоєння значення типу int, як і раніше, або шляхом присвоєння string.

Зверніть увагу, оператор неявного перетворення з нашої структури в string (рядок) не було створено. У деяких ситуаціяхце може створити невизначеність, з якою компілятор зможе розібратися. Щоб побачити це, додайте такі рядки коду:

Цей код компілюється без проблем. Тепер випробуйте цей код:

Компілятор видасть таку помилку: "Невизначеність виклику між наступними методами чи властивостями". Консоль успішно прийме int або string (і багато інших типів, звичайно), тому можна розраховувати, що консоль знатиме, що саме використовувати? Рішення – використовувати оператор явного перетворення.

Явне перетворення

Таке перетворення означає, що код має виконуватися явне приведення типів. Замініть ключове слово implicit на explicit в останньому операторі перетворення, щоб він виглядав так.

Тепер можна повертати рядкове значення, але тільки у разі явного перетворення типу на string.

Отримуємо очікуваний результат.

Порівняння перетворень

implicit значно зручніше, ніж explicit, тому що не потрібно явно перетворювати типи в коді, але як можна було побачити раніше, при цьому можуть виникати проблеми. Потрібно враховувати й інші речі: якщо об'єкти, що передаються в будь-якому напрямку, будуть так чи інакше округлені або усічені, то необхідно використовувати явне перетворення, щоб людина, яка використовує ваш об'єкт, не була викрита в тому, що дані відкидаються.

Уявіть, що виконується серія складних обчислень з десятковими дробами, і дозволяється неявне перетворення структури на десятковий дріб для зручності, але поле, де вона зберігається, цілочисленне. Користувач справедливо став би припускати, що будь-який об'єкт, що неявно передається в десятковий дріб, збереже точність, але наша структура втратила б всі десяткові розряди, що може призвести до отримання невірного результатуобчислень!

Якщо користувачеві необхідно явно перетворювати в/з описаної структури, слід розмістити попередження. У цій ситуації може бути найкращим варіантом створити незручність для користувача і не мати ні явний, ні неявний, щоб вони були змушені приводити до int, щоб використовувати описану структуру, і тоді не буде непорозуміння.

Є ще інші проблеми – виконайте ретельний аналіз перед використанням implicit. У разі сумніву, використовуйте explicit, або взагалі не реалізуйте оператор перетворення для цього типу.

Бінарні оператори

Бінарні оператори приймають два аргументи. Можуть бути перевантажені такі оператори: +, -, *, /, %, &, , ^, >.

Примітка: Важливо, щоб ви не робили нічого несподіваного під час використання операторів, двомісних або інших.

Зазвичай, ці оператори досить логічні. Спочатку розглянемо +. Він зазвичай обчислює суму двох аргументів.

Тому, залежно від ситуації, можна робити все, що логічно необхідно.

Уявіть структуру, що зберігає два цілих числа, і у вас є два її екземпляри, які ви хочете скласти. Логічно було б скласти відповідні цілі числа у кожному екземплярі. Ви також можете хотіти додати лише одне ціле число до обох значень. Структура, що вийшла, могла б виглядати приблизно так.

Цю структуру можна використовувати приблизно так:

Ви можете виконувати аналогічні дії з усіма двомісними операціями, що перевантажуються.

Унарні оператори

Унарні оператори приймають лише один аргумент. Можуть бути перевантажені такі оператори: +, -, !,

, ++, -, true, false. Для більшості типів не потрібно реалізовувати всі ці оператори, тому реалізуйте лише необхідні вам!

Простий приклад використання ++ для описаної вище структури:

Примітка: унарні оператори ++ і -- повинні (для стандартних операцій) змінювати цілі значення вашої структури і повертати той самий екземпляр, а не новий екземпляр з новими значеннями.

Оператори порівняння

Оператори порівняння приймають два аргументи. Можуть бути перевантажені такі оператори. [==, !=], [], [=]. Вони згруповані у квадратних дужках, тому що їх потрібно реалізовувати попарно.

При використанні == and != також необхідно замінити Equals(object o) та GetHashCode().

Оператори порівняння зазвичай повертають результат логічного типу, хоч це не обов'язково, але пам'ятайте, що не варто шокувати кінцевого користувача!

Інші оператори

Залишилися умовні оператори, &, , та оператори присвоєння, +=, -=, *=, /=, %=, &=, =, ^=, >=. Вони не перевантажуються, а обчислюються за допомогою двомісних операторів. Іншими словами, забезпечте наявність двомісних операторів і отримайте їх безкоштовно!

Висновок

Сподіваємося, що це введення в навантаження операторів було корисним та інформативним. Якщо ви не використовували її раніше, спробуйте її в роботі - вона може виявитися для вас корисним інструментом.

  • Не зловживайте ними. Якщо оператор або перетворення точно ніколи не знадобляться, або не очевидно, яким буде результат, не реалізуйте його.
  • Не використовуйте їх неправильно. Можна зробити так, щоб ++ зменшував значення, але цього безумовно не потрібно робити!
  • Будьте дуже обережні при неявному перетворенні ваших класів чи структур на інші типи.
  • Пам'ятайте, що структури – це типи значення, а класи – це типи посилань. Тому вони по-різному обробляються у вашихперевантажених методів.