JavaRush /Курси /C# SELF /Nullable-типи для значущих типів

Nullable-типи для значущих типів

C# SELF
Рівень 14 , Лекція 0
Відкрита

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
Оголошення nullable-типу за допомогою ?

Коли компілятор 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-тип Приклад запису
int
int?
int? a = null;
double
double?
double? x = 3.14;
bool
bool?
bool? ok = null;
DateTime
DateTime?
DateTime? d = null;

Можна записати й довше, використовуючи клас-обгортку:


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} градусів");
}
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ