0Pricing
C# Academy · Lekcja

Włączanie i poznawanie NRT

Włącz kontekst nullable, poznaj różnice między nullable i non-nullable reference types oraz odczytuj ostrzeżenia kompilatora.

Włączanie i poznawanie NRT to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.

Błąd za miliard dolarów

Tony Hoare (wynalazca wartości null) nazwał go swoim „błędem za miliard dolarów”. W języku C# każdy typ referencyjny mógł domyślnie mieć wartość null, przez co NullReferenceException stał się najczęstszą przyczyną awarii w czasie działania. Nullable Reference Types (NRT) rozwiązują ten problem na poziomie kompilatora.

Włączanie kontekstu nullable

Włącz NRT globalnie w pliku projektu albo dla pojedynczego pliku za pomocą dyrektyw. Po włączeniu kompilator domyślnie traktuje typy referencyjne jako non-nullable.

<!-- In .csproj — enable for the whole project -->
<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

// Or per-file:
#nullable enable
// ... code with NRT warnings
#nullable disable
// ... code without NRT warnings

Typy referencyjne non-nullable a nullable

Przy włączonym NRT: string oznacza wartość nigdy niebędącą null, a string? oznacza wartość, która może być null. Kompilator ostrzega, gdy przypisujesz null do typu non-nullable lub dereferencjonujesz typ nullable bez sprawdzenia.

#nullable enable

string nonNull = "Hello";    // OK
string? maybeNull = null;    // OK — explicitly nullable

nonNull = null;              // WARNING: CS8600
Console.WriteLine(maybeNull.Length); // WARNING: CS8602 — may be null

// Fix:
if (maybeNull is not null)
    Console.WriteLine(maybeNull.Length); // safe

Analiza przepływu

Kompilator wykonuje analizę przepływu — śledzi stan wartości null w instrukcjach warunkowych i gałęziach, wyciszając ostrzeżenia, gdy może dowieść, że wartość nie jest null.

string? name = GetName();

// After null check, name is treated as non-null:
if (name is not null)
    Console.WriteLine(name.Length);  // no warning

// Same with early return:
if (name is null) return;
Console.WriteLine(name.Length);     // no warning — null was returned

// Pattern matching:
if (name is string s)
    Console.WriteLine(s.ToUpper()); // no warning

Ostrzeżenia dotyczące nullable: CS8600–CS8629

Najważniejsze ostrzeżenia NRT, które należy rozumieć: CS8600 (przypisanie null do typu non-nullable), CS8602 (dereferencja wartości, która może być null), CS8603 (zwrócenie null dla typu non-nullable), CS8618 (niezainicjalizowane pole non-nullable).

#nullable enable

public class Order
{
    public string CustomerName { get; set; } // CS8618: not initialized
    public string? TrackingNumber { get; set; } // OK — nullable

    public Order(string name)
    {
        CustomerName = name; // now initialized — CS8618 gone
    }
}

string? s = GetValue();
Console.WriteLine(s.Length); // CS8602: s might be null

Inicjalizowanie pól non-nullable

CS8618 pojawia się, gdy pole lub właściwość non-nullable nie zostanie ustawiona w konstruktorze. Możliwe rozwiązania to inicjalizacja w deklaracji, wymaganie wartości za pomocą konstruktora albo użycie operatora ignorowania wartości null we wzorcach DI/ORM.

// Option 1: Initialize in declaration
public string Name { get; set; } = "";

// Option 2: Require via constructor
public class Product
{
    public string Name { get; }
    public Product(string name) => Name = name;
}

// Option 3: null-forgiving for ORM entities
public string Name { get; set; } = null!;
// null! tells compiler "trust me, this will be set by EF Core"

Nullable w typach generycznych

Parametry typów generycznych można ograniczyć do typów non-nullable. Ograniczenie where T : notnull gwarantuje, że T nigdy nie będzie mieć wartości null.

// Without constraint: T could be nullable
public T? Find<T>(int id) { ... }

// With notnull: T must be a non-nullable type
public T FindRequired<T>(int id) where T : notnull
{
    var result = InternalFind<T>(id);
    return result ?? throw new KeyNotFoundException();
}

// T? is meaningful only when T is known to be non-nullable
public T? FindOrDefault<T>(int id) where T : class { ... }

Nullable w interfejsach i przesłonięciach

Podczas implementowania interfejsu lub przesłaniania metody adnotacje dotyczące nullowalności muszą się zgadzać. Kompilator sprawdza, czy implementacje są co najmniej tak samo non-nullable, jak deklaruje to interfejs.

public interface IRepository<T>
{
    T? FindById(int id);       // may return null
    T GetOrThrow(int id);      // never null
}

// Implementation:
public class ProductRepo : IRepository<Product>
{
    public Product? FindById(int id)  => _db.Find(id);
    public Product  GetOrThrow(int id) =>
        _db.Find(id) ?? throw new KeyNotFoundException();
}

Nullable Value Types a Nullable Reference Types

Nie należy mylić int? (Nullable<int> — opakowanie typu wartości istniejące już przed C# 8) z string? (adnotacja NRT — używana wyłącznie w czasie kompilacji, bez narzutu w czasie działania).

// Nullable value type (runtime Nullable<int>)
int? age = null;
bool hasValue = age.HasValue;
int value  = age.GetValueOrDefault();

// Nullable reference type (compile-time annotation only)
string? name = null;
// string? has NO runtime wrapper — it's just a compile-time hint
// null check is still needed at runtime
if (name is not null)
    Console.WriteLine(name.ToUpper());

Wyciszanie ostrzeżeń za pomocą !

Operator ignorowania wartości null (!) wycisza konkretne ostrzeżenie dotyczące nullable, gdy ma Pan lub Pani większą wiedzę niż kompilator. Należy używać go oszczędnie i dokumentować, dlaczego jest bezpieczny.

// Use ! when you know the value cannot be null
var user = _db.Users.FirstOrDefault(u => u.Email == email);
var name = user!.Name; // user is guaranteed by business logic

// EF Core navigation properties set by the framework:
public class Order
{
    public Customer Customer { get; set; } = null!; // set by EF Core
}

Praktyczny przykład: odpowiedź API z wartościami nullable

Opakowanie API, w którym pola mogą występować w odpowiedzi JSON albo nie — adnotacje nullable jasno komunikują zamierzone zachowanie.

#nullable enable

public class WeatherResponse
{
    public string City { get; set; } = "";     // always present
    public double Temperature { get; set; }     // always present
    public string? Description { get; set; }    // optional field
    public string? AlertMessage { get; set; }   // only when there's an alert
}

// Consumer:
var weather = await api.GetWeatherAsync("London");
Console.WriteLine(weather.City);            // safe
Console.WriteLine(weather.Description?.ToUpper() ?? "N/A"); // safe null-handling

Szybkie sprawdzenie

Jaki narzut w czasie działania mają Nullable Reference Types (string?) w porównaniu ze zwykłymi typami referencyjnymi (string)?

Podsumowanie: włączanie i rozumienie NRT

Najważniejsze informacje:

  • Włącz globalnie za pomocą <Nullable>enable</Nullable> w pliku .csproj
  • string = nigdy nie jest null; string? = może być null
  • Analiza przepływu śledzi stan null w gałęziach i wartościach zwracanych
  • Adnotacje NRT działają wyłącznie w czasie kompilacji — nie mają żadnego narzutu w czasie działania
  • CS8618: inicjalizuj pola non-nullable w konstruktorze lub użyj = null! dla ORM
  • Używaj ! oszczędnie — dokumentuj, dlaczego operator ignorowania wartości null jest bezpieczny

Często zadawane pytania

Czy lekcja „Włączanie i poznawanie NRT” jest bezpłatna?

Tak — pełny tekst „Włączanie i poznawanie NRT” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Włączanie i poznawanie NRT”?

Włącz kontekst nullable, poznaj różnice między nullable i non-nullable reference types oraz odczytuj ostrzeżenia kompilatora. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C# Academy?

Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Włączanie i poznawanie NRT”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?

Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Włączanie i poznawanie NRT
  2. Adnotacje: ?, !, MaybeNull i NotNull
  3. Operatory null-conditional i null-coalescing
  4. Migracja bazy kodu do NRT
← Powrót do C# Academy