OOP Course
Сьогодні

Підрозділ 2.9

Перетворення базових типів

Описує перетворення між базовими типами, різницю між розширювальними і звужувальними перетвореннями та можливі втрати даних.

2.9. Перетворення базових типів

Програми рідко працюють лише з даними одного типу. На практиці часто виникає потреба передати значення одного типу туди, де очікується інший: зберегти byte у змінну int, або навпаки — помістити int у byte. Саме тут вступає в дію перетворення типів (type conversion). Розуміння того, коли і як відбуваються перетворення, є необхідним для написання коректного і передбачуваного коду.

Перетворення базових типів

Чому потрібне перетворення типів

Розглянемо просту ситуацію: є змінна типу byte зі значенням 4. Ми хочемо додати до неї число і записати результат у нову byte-змінну:

Але якщо спробувати записати результат у byte-змінну:

byte a = 4;
byte b = a + 70;  // помилка компіляції!

Компілятор відмовить у цьому навіть попри те, що число 74 чудово вписується в діапазон byte (0–255). Причина полягає в тому, що результат операції додавання для цілих типів з розрядністю не більше int завжди повертається як int — 4-байтове ціле. Компілятор не може без явної вказівки записати 4-байтовий результат у 1-байтову змінну, оскільки це потенційно небезпечна операція.

Операція явного приведення типу

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

(цільовий_тип) вираз

Застосуємо до попереднього прикладу:

Дужки навколо (a + 70) важливі: без них операція приведення застосується тільки до a, а потім до результату (byte)a + 70 знову буде складання двох значень і повернення int. Зовнішні дужки гарантують, що спочатку обчислюється вся сума, і вже потім виконується приведення всього результату до byte.

Розширювальні перетворення (widening)

Розширювальне перетворення (widening conversion) відбувається, коли значення типу з меншим розміром у пам'яті записується у змінну типу з більшим розміром. Наприклад, byte займає 1 байт (8 біт), а int — 4 байти (32 біти). Записати byte в int — це розширення: місця більше, ніж потрібно, і дані точно поміщаються без втрат.

Розширювальне перетворення виконується автоматично і не потребує жодних явних дій з боку розробника — компілятор сам додає необхідні нулі зліва:

Внутрішньо відбувається доповнення нулями (zero extension) для беззнакових типів: ліві біти заповнюються нулями. Для знакових типів відбувається sign extension: ліві біти заповнюються копією знакового біту — нулями для позитивних чисел і одиницями для від'ємних. Саме завдяки цьому від'ємні числа залишаються від'ємними після розширення:

Звужувальні перетворення (narrowing)

Звужувальне перетворення (narrowing conversion) — це зворотна ситуація: значення типу з більшим розміром записується в змінну меншого типу. Наприклад, int у byte, або double у int. Тут вже немає гарантії, що значення поміститься без втрат — старші біти просто відкидаються.

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

При перетворенні double на int відкидається дробова частина — без округлення:

Важливо усвідомити: (int) не округлює, а відкидає дробову частину. Число 36.9 перетвориться не в 37, а в 36. Для коректного математичного округлення до найближчого цілого потрібно використовувати Math.Round.

Автоматичні безпечні перетворення

Компілятор C# виконує розширювальні перетворення автоматично — тільки в тих випадках, коли вони гарантовано безпечні. Ось основні ланцюжки таких перетворень:

byte  → short  → int  → long  → decimal
int   → double
short → float  → double
char  → int    → long

Звернімо увагу на особливість пари double і decimal: незважаючи на те, що decimal займає 16 байт (проти 8 у double) і забезпечує вищу точність — перетворення між ними не є автоматичним ні в одному напрямку. Обидва типи потребують явного приведення:

Причина полягає в тому, що double і decimal мають фундаментально різну внутрішню організацію: double — це IEEE 754 бінарне число з плаваючою точкою, а decimal — десяткове число з фіксованою точністю. Пряме перетворення між ними може призвести до незначних похибок, тому компілятор змушує розробника зробити це явно — щоб він розумів, що таке перетворення відбувається.

Приклад: обчислення з перетвореннями типів

Розроблено Tomka Yurii · © 2026 ·