1. Вступ
Уявімо таку ситуацію. Ви зберігаєте дані про котів у коді. І хочете, окрім, наприклад, віку кота, фіксувати ще й стани на кшталт «немає даних», «вік невідомий». Можна було б записувати 0, але нуль — це цілком можливий вік.
int age = 0; // Вік кота. Але що таке 0? Це кошеня чи "невідомо"?
Проблема в тому, що int зберігає лише цілі числа, а стану «немає даних» серед них не існує. Якщо ж вік — рядок, там можна було б присвоїти null. Але з числами такий фокус не проходить:
int age = null; // Помилка! Не можна присвоїти null змінній типу int.
Це пов’язано з тим, що значущі типи (struct-и типу int, double, DateTime, bool) завжди щось зберігають. У них немає «порожнього» стану (на відміну від посилальних типів, де null — це просто відсутність об’єкта).
Приклад із життя:
Намагаючись обійти це обмеження, програмісти інколи вигадують «особливі» значення: наприклад, -1 або int.MaxValue для «немає значення». Але це незручно й небезпечно: легко переплутати реальне значення із заглушкою.
2. Nullable-тип: тип, що може мати «null»
Ідея
А що, якщо дозволити звичайним числам (і взагалі всім значущим типам) зберігати не тільки значення, а й null?
У C# це реалізовано за допомогою спеціального класу-обгортки Nullable<T>. Такий клас може містити або значення типу T, або нічого (тобто null).
Синтаксис
int? age = null; // Змінна age може бути і числом, і null
Коли компілятор C# бачить такий код, він фактично сприймає його як:
Nullable<int> age = new Nullable<int>(); // Змінна age не містить значення
Або, якщо треба явно вказати значення:
Nullable<int> age = new Nullable<int>(42); // age містить число 42
Таким чином, знак питання перетворює будь-який значущий тип на його nullable-версію:
- int? (те саме, що Nullable<int>)
- double?
- DateTime?
- і будь-які інші структури
Nullable-обгортки для найпоширеніших структур
| Звичайний тип | Nullable-тип | Приклад запису |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Можна записати й довше, використовуючи клас-обгортку:
Nullable<int> pages = null;
Але запис int? — коротший, тож зручніший.
3. Як використовувати nullable-тип
Присвоєння й перевірка
Присвоєння null працює так само, як і з посилальними типами:
Оголошення й присвоєння
int? temperature = null;
temperature = 25;
Перевірка на null
Як дізнатися, чи є в нашому nullable-типі справжнє значення?
if (temperature != null)
{
Console.WriteLine($"Температура: {temperature}");
}
else
{
Console.WriteLine("Немає даних про температуру");
}
Властивості Nullable-типів: .HasValue і .Value
У nullable-типів є дві важливі властивості:
- .HasValue — повертає true, якщо у змінній є значення (не null).
- .Value — саме значення, якщо воно є (інакше буде виняток).
int? temperature = null;
if (temperature.HasValue) // Винятку не буде!
{
Console.WriteLine($"Температура: {temperature.Value}°C");
}
else
{
Console.WriteLine("Температура невідома");
}
Цей код працюватиме: до властивості HasValue можна звертатися завжди. А от намагатися отримати .Value, коли значення відсутнє, — погана ідея: буде згенеровано виняток InvalidOperationException. Перевіряйте наявність значення завчасно!
Скорочений синтаксис виведення
У багатьох випадках достатньо просто використати змінну nullable-типу — і C# сам усе зрозуміє:
int? hours = null;
Console.WriteLine(hours); // виведе порожній рядок
hours = 10;
Console.WriteLine(hours); // виведе 10
4. Операції й nullable-типи: арифметика, порівняння, перетворення
Операції: що відбувається?
Якщо один із операндів — nullable, результат також буде nullable.
А якщо один із операндів дорівнює null, результат теж буде null.
int? a = 5;
int? b = null;
int? sum = a + b; // sum == null
int? c = 10;
int? d = 15;
int? total = c + d; // total == 25
Операції порівняння
Можна порівнювати nullable-типи зі звичайними числами:
int? score = null;
if (score > 0) Console.WriteLine("Чудовий результат!");
else Console.WriteLine("Немає результату"); // Спрацює ця гілка
Якщо score дорівнює null, умова score > 0 буде false.
Явне й неявне перетворення
- Із звичайного типу у nullable — автоматично.
- З nullable у звичайний — лише якщо значення не null (інакше буде виняток).
int? x = 3;
int y = x.Value; // Ок, якщо x не null
// Неявно
int? z = y;
5. Nullable й методи: параметри та значення, що повертаються
Повернення nullable-значень
Якщо метод не завжди може повернути результат, використовуйте nullable-типи як значення, що повертається:
// Метод шукає користувача за логіном і повертає його вік, якщо знайшов, інакше null
int? FindUserAge(string login)
{
// ... тут логіка пошуку
return null; // якщо не знайдено
}
Nullable-параметри
Можна приймати nullable-значення у параметри, щоб явно показати: «може бути, а може й не бути».
void PrintTemperature(int? temp)
{
if (temp == null)
Console.WriteLine("Температура невідома");
else
Console.WriteLine($"Температура: {temp} градусів");
}
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ