0Pricing
C# Academy · Lekcja

Adnotacje: ?, !, MaybeNull i NotNull

Używaj ? dla typów nullable, ! do tłumienia ostrzeżeń o null oraz atrybutów takich jak MaybeNull i NotNullWhen do precyzyjnej analizy przepływu.

Adnotacje: ?, !, MaybeNull i NotNull to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 2 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.

Poza ? i !: atrybuty dotyczące nullowalności

Podstawowa adnotacja ? i sprawdzanie wartości null obsługują większość przypadków, ale niektóre wzorce wymagają bardziej wyrazistych adnotacji. Przestrzeń nazw System.Diagnostics.CodeAnalysis udostępnia atrybuty, które zapewniają kompilatorowi dokładniejsze informacje o przepływie wartości null.

MaybeNull i NotNull

[MaybeNull] informuje kompilator, że typ zwracany jako non-nullable może w praktyce mieć wartość null (np. w metodzie generycznej). [NotNull] gwarantuje, że parametr nullable nie będzie mieć wartości null po zakończeniu wywołania.

using System.Diagnostics.CodeAnalysis;

// [MaybeNull]: return might be null even though T is non-nullable
[return: MaybeNull]
public T Find<T>(int id)
{
    // Returns default(T) which is null for reference types
    return _cache.TryGetValue(id, out var val) ? val : default!;
}

// [NotNull]: after this call, output is guaranteed non-null
public static void EnsureNotNull<T>(
    [NotNull] ref T? value,
    T defaultValue) where T : class
{
    value ??= defaultValue;
}

NotNullWhen: warunkowa wartość non-null

[NotNullWhen(true)] informuje kompilator, że parametr wyjściowy nie ma wartości null, gdy metoda zwraca true. Tak właśnie opisywany jest standardowy wzorzec TryParse.

using System.Diagnostics.CodeAnalysis;

public static bool TryParseEmail(
    string? input,
    [NotNullWhen(true)] out string? email)
{
    if (input?.Contains('@') == true)
    {
        email = input.Trim().ToLower();
        return true;
    }
    email = null;
    return false;
}

// Usage — no warning after true check:
if (TryParseEmail(raw, out var email))
    Console.WriteLine(email.Length); // safe — email is non-null here

MaybeNullWhen: warunkowa wartość null

[MaybeNullWhen(false)] działa odwrotnie — wynik może mieć wartość null, gdy metoda zwraca false. Atrybut ten jest używany we wzorcach słownikowej metody TryGetValue.

// This is how Dictionary<K,V>.TryGetValue is annotated:
public bool TryGetValue(
    TKey key,
    [MaybeNullWhen(false)] out TValue value)
{ ... }

// Usage:
if (!dict.TryGetValue("key", out var value))
    return; // early return — value is null in this branch

Console.WriteLine(value.Length); // safe after the guard

AllowNull i DisallowNull

[AllowNull] na właściwości non-nullable pozwala wywołującym przekazywać do niej wartość null (np. w setterze, który zamienia null na pusty ciąg). [DisallowNull] zabrania wartości null dla typu nullable.

public class Config
{
    private string _name = "";

    // Allow setting null (setter converts null -> empty)
    [AllowNull]
    public string Name
    {
        get => _name;
        set => _name = value ?? "";
    }

    // Getter always returns non-null: fine
    // Setter accepts null: [AllowNull] tells compiler that's OK
}

NotNullIfNotNull: propagowanie nullowalności

[NotNullIfNotNull(paramName)] oznacza: jeśli parametr X nie ma wartości null, wartość zwracana również nie ma wartości null. Jest to przydatne w funkcjach transformujących.

[return: NotNullIfNotNull(nameof(value))]
public static string? Normalize(string? value)
{
    return value?.Trim().ToLower();
}

// Usage:
string  name = "  Alice  ";
string  norm1 = Normalize(name)!; // guaranteed non-null
string? raw   = GetRaw();
string? norm2 = Normalize(raw);   // still nullable (raw might be null)

DoesNotReturn

[DoesNotReturn] oznacza metodę, która zawsze zgłasza wyjątek. Kompilator wie, że kod po wywołaniu jest nieosiągalny, dzięki czemu pomija nieuzasadnione ostrzeżenia dotyczące wartości null.

using System.Diagnostics.CodeAnalysis;

[DoesNotReturn]
public static void ThrowNotFound(int id)
    => throw new KeyNotFoundException($"Entity {id} not found");

// Usage — no null warning after the call:
var order = _db.Orders.Find(id);
if (order is null) ThrowNotFound(id);

Console.WriteLine(order.Id); // no CS8602 — compiler knows ThrowNotFound threw

MemberNotNull: warunek końcowy dla pól

[MemberNotNull] informuje kompilator, że metoda gwarantuje, iż określone pola nie będą mieć wartości null po jej zakończeniu. Jest to przydatne w pomocniczych metodach leniwej inicjalizacji.

public class DataLoader
{
    private string? _data;

    [MemberNotNull(nameof(_data))]
    private void EnsureLoaded()
    {
        if (_data is null)
            _data = LoadFromFile();
    }

    public string GetData()
    {
        EnsureLoaded();
        return _data; // no CS8603 — compiler knows _data is set
    }
}

Łączenie adnotacji

Atrybuty można łączyć, aby precyzyjnie definiować kontrakty API. Oto pomocnicza metoda fluent guard, która łączy kilka atrybutów.

public static class Guard
{
    [return: NotNull]
    public static T NotNull<T>(
        [NotNull][AllowNull] T? value,
        [CallerArgumentExpression(nameof(value))] string? name = null)
        where T : class
    {
        ArgumentNullException.ThrowIfNull(value, name);
        return value;
    }
}

// Usage:
var product = Guard.NotNull(await _repo.FindAsync(id));
Console.WriteLine(product.Name); // no warning

Operatory wartości null: ?. ?? ??=

Operatory null-conditional (?.), null-coalescing (??) oraz null-coalescing assignment (??=) pozwalają pisać zwięzły kod bezpieczny dla wartości null, bez rozbudowanych instrukcji if.

string? name = GetName();

// ?. safe navigation
int? len = name?.Length;
string? upper = name?.ToUpper().Trim();

// ?? default value
string display = name ?? "Anonymous";

// ??= lazy init
name ??= "Default";

// Chaining
string result = user?.Profile?.DisplayName ?? user?.Name ?? "Guest";

Szybkie sprawdzenie

Co atrybut [NotNullWhen(true)] na parametrze out przekazuje kompilatorowi?

Podsumowanie: adnotacje dotyczące nullowalności

Najważniejsze informacje:

  • [NotNullWhen(true)]: wzorzec TryParse — wartość non-null przy zwrocie true
  • [MaybeNull]: typ non-nullable może mimo to zwrócić null (domyślne wartości generyczne)
  • [DoesNotReturn]: metoda zawsze zgłasza wyjątek — kod po niej jest nieosiągalny
  • [MemberNotNull]: metoda gwarantuje ustawienie pól po swoim zakończeniu
  • ?., ??, ??=: zwięzła, bezpieczna dla wartości null nawigacja i wartości domyślne

Często zadawane pytania

Czy lekcja „Adnotacje: ?, !, MaybeNull i NotNull” jest bezpłatna?

Tak — pełny tekst „Adnotacje: ?, !, MaybeNull i NotNull” 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 „Adnotacje: ?, !, MaybeNull i NotNull”?

Używaj ? dla typów nullable, ! do tłumienia ostrzeżeń o null oraz atrybutów takich jak MaybeNull i NotNullWhen do precyzyjnej analizy przepływu. Ć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 2 z 4.

Ile czasu zajmuje lekcja „Adnotacje: ?, !, MaybeNull i NotNull”?

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