1. Оператор об’єднання з null (??)
Ваші знання про nullable-типи не будуть повними без уміння писати лаконічні та безпечні конструкції. У C# для цього є два дуже корисні оператори: об’єднання з null (??) і null-умовний оператор (?.).
Оператор ?? дозволяє задати «значення за замовчуванням» для змінної, яка може бути null.
Уявіть, що у вас є імʼя користувача, яке може бути null. Якщо воно справді null, ви хочете показати «Гість». Приклад:
string userName = null;
string displayName = userName != null ? userName : "Гість";
Оператор ?? дозволяє записати це компактніше:
string userName = null;
string displayName = userName ?? "Гість";
Як це працює:
- Якщо вираз ліворуч не null, його значення повертається як результат.
- Якщо ліворуч null, використовується значення праворуч.
Ще приклади:
int? age = null;
int displayAge = age ?? -1; // -1 — значення за замовчуванням
Console.WriteLine(displayAge); // Виведе -1
string input = null;
string name = input ?? "Гість";
Console.WriteLine($"Привіт, {name}!"); // Привіт, Гість!
Ланцюжки операторів
Можна вкладати ?? у ланцюжок:
string result = str1 ?? str2 ?? "За замовчуванням";
Якщо str1 не null, використовується його значення. Інакше — str2. Якщо й він null, тоді — рядок «За замовчуванням».
2. null-умовний оператор (?.)
Ще одна типова ситуація — викликати метод або звертатися до властивості об’єкта лише тоді, коли сам об’єкт не null. Приклад:
User user = null;
string displayName = user != null ? user.Name : null;
Оператор ?. дозволяє записати це простіше:
User user = null;
string displayName = user?.Name;
Якщо об’єкт user дорівнює null, вираз не спричинить помилку, а просто поверне null.
Приклади:
User user = null;
// Без ?. — буде помилка
// Console.WriteLine(user.Name); // NullReferenceException
Console.WriteLine(user?.Name); // Безпечно — надрукує порожній рядок або нічого
Console.WriteLine(user?.GetProfileInfo()); // Аналогічно
User[] users = null;
int? count = users?.Length; // null, якщо users == null
Ланцюжки операторів
Можна будувати цілі «ланцюжки»:
string domain = company?.Director?.Email?.Split('@')?[1];
Якщо будь-яка частина шляху виявиться null, вираз поверне null, а не згенерує виняток.
Поєднання з ??
Оператори ?. і ?? чудово працюють разом:
string display = user?.Name ?? "Невідомо";
Якщо user або user.Name дорівнюють null, буде виведено «Невідомо».
3. Що таке ! і навіщо він потрібен
Оператор придушення попереджень про null
У сучасних версіях C# (якщо ввімкнено NRT) компілятор завбачливо попереджає, коли ви звертаєтеся до змінної, що може бути null. Але іноді ви впевнені, що все під контролем. Тоді використовується оператор придушення !.
string? possibleNull = GetUserNameMaybeNull();
Console.WriteLine(possibleNull.Length); // Попередження: можливий null
Console.WriteLine(possibleNull!.Length); // Компілятор мовчить, але якщо там null — буде виняток
Важливо: ! не захищає від помилок — він просто каже компілятору «повір мені». Якщо змінна справді дорівнює null, буде NullReferenceException.
Куди не варто його застосовувати
Не використовуйте ! навмання. Гарний стиль — мінімізувати його застосування. Краще перебудувати код так, щоб null був або неможливий, або явно оброблений.
4. default — як отримати «стандартне» значення типу
Іноді потрібно просто «скинути» змінну до початкового стану, особливо якщо не хочеться вручну писати 0, false чи null.
Для цього існує ключове слово default.
int a = default; // a == 0
bool flag = default; // flag == false
string s = default; // s == null
double? d = default; // d == null
У вашому мінізастосунку можна, наприклад, скинути імʼя чи вік:
userName = default; // null
userAge = default; // null (якщо userAge — int?)
5. Відмінності та нюанси: ?, !, default
Навіть досвідчені розробники іноді губляться, коли бачать ?, ! і default поруч. Розберімося, хто є хто.
? — ви дозволяєте null
- Для значущих типів: int? x — тепер x може бути null.
- Для посилальних типів: string? s — ви явно вказуєте, що змінна може бути null (у режимі NRT).
! — ви запевняєте, що null не буде
- user!.Name — ви обіцяєте компілятору, що user точно не null.
- Працює тільки в режимі Nullable Reference Types.
default — ви просите значення «за замовчуванням»
- int x = default; — x стане 0
- string s = default; — s стане null
Порівняння:
| Синтаксис | Що це? | Де працює? | До чого тут null? |
|---|---|---|---|
|
Значущий тип, що допускає null | Скрізь | Можна присвоїти null |
|
Посилальний тип, що допускає null (NRT) | У режимі NRT | Можна присвоїти null |
|
Оператор придушення попереджень про null | У режимі NRT | Придушує попередження |
|
Значення за замовчуванням | Скрізь | Для посилальних — це null |
6. Типові помилки під час використання ?, ! і default
Помилка № 1: вважають, що ! «лікує» null.
Насправді ! лише змушує компілятор не сваритися. Якщо значення таки виявиться null, програма впаде з NullReferenceException.
Помилка № 2: забувають перевіряти .HasValue перед використанням .Value.
Особливо це стосується int?, bool? та інших nullable-типів. Без такої перевірки можна отримати виняток InvalidOperationException.
Помилка № 3: застосовують default там, де потрібне «особливе» нульове значення.
Наприклад, 0 чи false можуть бути цілком допустимими значеннями, а не сигналами відсутності значення. Це може призвести до логічних помилок.
Помилка № 4: не використовують ? для посилальних типів у режимі NRT — або зловживають ним.
Хтось забуває про ?, і компілятор видає багато попереджень. А хтось, навпаки, всюди ставить ?, навіть там, де null ніколи не буває. І те, й інше знижує читабельність і надійність коду.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ