Підрозділ 8.2
Визначення інкременту та декременту
Розглядає перевантаження інкременту, декременту, операторів true і false та використання об'єкта як умови.
8.2. Визначення інкременту та декременту
Оператори ++ і -- — унарні, тобто вони приймають один операнд і повертають змінений стан. Для користувацьких типів їх можна перевантажити так само, як і будь-який інший оператор. Однак у них є важлива особливість, яку необхідно враховувати при визначенні.
Правило незмінності параметрів
Коли ми визначаємо оператор ++ або --, параметр методу представляє поточний об'єкт. Інтуїтивно здається, що потрібно просто змінити значення прямо у методі:
// НЕПРАВИЛЬНО — мутуємо параметр
public static BodyTemperature operator ++(BodyTemperature t)
{
t.Celsius += 0.1; // не можна: Celsius — readonly
return t;
}Це неправильний підхід з двох причин. По-перше, якщо властивість доступна лише для читання (що є стандартом для immutable-типів), такий код не скомпілюється. По-друге, і це головне: оператор не повинен мутувати свої параметри. Параметри — це входи, а не виходи. Правильне визначення — повернути новий об'єкт з оновленим значенням:
// ПРАВИЛЬНО — повертаємо новий об'єкт
public static BodyTemperature operator ++(BodyTemperature t)
{
return new BodyTemperature(t.Celsius + 0.1);
}Цей підхід відомий як «immutable update pattern»: замість зміни існуючого об'єкта ми створюємо новий із потрібним значенням. Він запобігає побічним ефектам і є стандартом при визначенні операторів у C#.
Синтаксис оператора інкременту та декременту
Оскільки ++ і -- — унарні оператори, вони приймають один параметр. Одне визначення охоплює і префіксну (++t), і постфіксну (t++) форму — компілятор сам реалізує різницю:
Math.Round(..., 1) тут необхідний через особливості арифметики чисел із плаваючою крапкою: 36.6 + 0.1 без округлення може дати 36.699999... замість 36.7.
Префіксна та постфіксна форми: як компілятор розрізняє їх
Хоча ми визначаємо один метод, поведінка t++ і ++t — різна. Різниця не в тому, як змінюється об'єкт, а в тому, яке значення повертається як результат виразу. Компілятор реалізує це самостійно на основі одного нашого визначення.

Побачимо різницю в дії:
При постфіксному t1++ компілятор виконує три дії: зберігає поточний об'єкт у тимчасову змінну, замінює t1 результатом operator++(t1), а як результат виразу повертає тимчасову (стару) копію. При префіксному ++t3 — викликає operator++(t3), присвоює результат t3 і повертає його ж.
Ця різниця важлива лише тоді, коли результат виразу використовується: t2 = t1++ та t2 = ++t1 поводяться по-різному. Якщо ж ми пишемо просто t++; або ++t; в окремому рядку — різниці немає.
Оператори true та false
Окрема пара унарних операторів — true і false. Вони визначаються, коли ми хочемо використовувати об'єкт нашого класу безпосередньо як умову в if, while або тернарному операторі — без явного виклику методу чи порівняння з bool.
У клінічній системі природним кандидатом є клас MedicalDevice. Пристрій може бути активним або офлайн, і зручно писати if (device) замість if (device.IsOnline):
Оператори true і false завжди визначаються парою — компілятор вимагає наявності обох. Оператор ! не є обов'язковим, але без нього конструкція if (!device) не компілюється. Семантично !device збігається з оператором false — обидва перевіряють, що пристрій «не true».
Повний приклад: температурний моніторинг
Об'єднаємо ++, -- і true/false у реалістичному клінічному сценарії — автоматичному відстеженні температури пацієнта після лікування:
У цьому прикладі if (!temp) перевіряє аномальний стан, а if (temp) у циклі фіксує повернення до норми. Код читається природно і не вимагає явного звертання до IsNormal або IsFever у кожній умові.