JavaRush /Курси /C# SELF /Аналіз попереджень компілятора

Аналіз попереджень компілятора

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

1. Вступ

Уявіть, що програмний код — це вистава, а змінні — актори. Кожного разу, коли змінна потрапляє у нову сцену (блок коду), вона може бути «живою» (має значення) або «привидом» (null). Але ось халепа: глядач — компілятор — дивиться виставу не наживо, а за сценарієм. Він бачить, де акторам видають ролі (присвоюють значення), а де вони зникають (стають null). Та зазирнути в реальність (виконання програми) він не може — лише здогадуватися з тексту сценарію!

Статичний аналіз потоку даних — це процес, за якого компілятор перевіряє код ще до запуску програми, щоб з’ясувати, чи не намагається хтось зіграти сцену з «падінням NullReferenceException».

Суть процесу

Починаючи з C# 8.0 та появи режиму Nullable Reference Types (NRT), компілятор аналізує, чи може змінна опинитися null у якійсь гілці виконання. І якщо так — він одразу повідомляє про це. Він не дозволить вам «використати імʼя актора, який раптом став привидом».

2. Як читати попередження компілятора

Основні категорії попереджень

Коли активовано режим NRT, компілятор особливо уважно стежить за змінними посилального типу (string, User, масиви, обʼєкти). Ось найпоширеніші типи попереджень:

  • Possible null assignment — ви намагаєтеся присвоїти значення, яке може бути null, змінній, що null не допускає.
  • Dereference of a possibly null reference — ви звертаєтеся до члена обʼєкта, але компілятор сумнівається, що той не дорівнює null.
  • Possible null return — метод може повернути null, хоча за сигнатурою має повертати значення.
  • Nullable object must have a value — ви використовуєте .Value у nullable-типу без перевірки.
  • Unassigned non-nullable property — ви не надали значення полю/властивості, що не допускає null.

Зазвичай у IDE (наприклад, у Rider або Visual Studio) такі попередження підкреслюються хвилястою лінією, а коли наводите вказівник миші — з’являється пояснення.

Приклад типового попередження


#nullable enable

string? possibleNull = GetString();

// Ой-ой, компілятор попереджає: "Dereference of a possibly null reference"
int length = possibleNull.Length; // Попередження!

Компілятор не впевнений, що possibleNull не дорівнює null, а отже — звернення до .Length може призвести до винятку.

Якщо метод виглядає так:

string? GetString() { ... }

А ви пишете:

string nonNullable = GetString();

Тоді отримаєте попередження: можливе небезпечне присвоєння null змінній, що не допускає null.

3. Компілятор аналізує ваш код

Приклад 1: прямолінійне присвоєння


string? name = null;
Console.WriteLine(name.Length); // Попередження: можливе звернення до null

Тут усе очевидно: name дорівнює null, тож спроба звернення до властивості — потенційно небезпечна.

Приклад 2: перевірка на null


string? name = GetUserName();

if (name != null)
{
    Console.WriteLine(name.Length); // Усе гаразд!
}
else
{
    Console.WriteLine("Імʼя не вказано!");
}

Компілятор розуміє, що всередині блоку if (name != null) змінна вважається безпечною. Це і є flow analysis — аналіз потоку значень залежно від умов.

4. Компілятор не може зазирнути «усередину методу»

Він дивиться лише на сигнатуру: якщо зазначено, що метод повертає string?, він вірить, що може бути null, навіть коли всередині завжди повертається рядок.

Приклад 3: повернення значення з методу


string? GetMaybeName(bool useName)
{
    if (useName)
        return "Code Jedi";
    else
        return null;
}

void PrintLength()
{
    string? name = GetMaybeName(false);

    Console.WriteLine(name.Length); // Попередження!
}

Компілятор бачить, що GetMaybeName може повернути null, тому просить вас бути обережнішими.

5. Поширені сценарії та попередження

Присвоєння nullable у non-nullable змінну


string? maybeUser = GetUser();
string alwaysUser = maybeUser; // warning: possible null assignment

Щоб прибрати попередження:


string alwaysUser = maybeUser ?? "Гість";

Тепер завжди буде рядок — або з методу, або «Гість».

Забули перевірити .HasValue у nullable value-типу


int? age = GetAge();
int realAge = age.Value; // warning: можливе звернення до null

Можна так:


if (age.HasValue)
    Console.WriteLine(age.Value);

Або:


int realAge = age ?? -1;

Автоматичні властивості без ініціалізації


class User
{
    public string Name { get; set; } // warning: не ініціалізовано!
}

Варіанти розв’язання:


public string Name { get; set; } = "Без імені";

або


public string? Name { get; set; }

6. Складні випадки аналізу потоку

Аналіз у циклах


string? text = null;

while (text == null)
{
    text = Console.ReadLine();
}

Console.WriteLine(text.Length); // усе гаразд: компілятор це зрозумів!

Різні шляхи присвоєння


string? name;
if (Random.Shared.Next() % 2 == 0)
    name = "Alice";
else
    name = null;

Console.WriteLine(name.Length); // warning: possible null dereference

Компілятор аналізує всі гілки. Якщо бодай в одній — null, він попереджає.

7. Як «заспокоїти» компілятор (і коли цього не варто робити)

Іноді ви впевнені, що значення не може бути null, але компілятор не вірить:


string? value = GetSomething();
if (value == null)
    throw new Exception("Очікувалося значення!");

Console.WriteLine(value.Length); // Попередження зникає

Або ви користуєтеся оператором !:


string? value = GetSomething();
Console.WriteLine(value!.Length); // Без попереджень, але потенційно небезпечно

Це називають null-forgiving operator. Він нічого не перевіряє — лише «каже» компілятору: «заспокойся, усе під контролем». Але якщо ви помилитеся, під час виконання отримаєте виняток.

1
Опитування
Nullable-типи, рівень 14, лекція 4
Недоступний
Nullable-типи
Vstup do Nullable Reference Types
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ