Bezpieczeństwo null w języku C#

Tip

Ten artykuł jest częścią sekcji Podstawy dla deweloperów, którzy już znają co najmniej jeden język programowania i uczą się języka C#. Jeśli dopiero zaczynasz programować, najpierw zacznij od samouczków Wprowadzenie .

Pochodzisz z Java czy C++? Język C# zapewnia bezpieczeństwo wartości null w czasie kompilacji za pomocą typów odwołań dopuszczanych do wartości null. Cel jest podobny do Java @NonNull adnotacji, ale wymuszany przez kompilator. Język C# ma również dedykowane operatory, takie jak ?. i ?? które sprawiają, że wyrażenia bezpieczne dla wartości null są zwięzłe.

null reprezentuje brak wartości. Podczas próby uzyskania dostępu do elementu członkowskiego w odwołaniu null przez wywołanie metody lub odczytanie właściwości środowisko uruchomieniowe zgłasza błąd NullReferenceException:

// Accessing a member on null throws NullReferenceException at runtime:
// string? name = null;
// int length = name.Length; // throws NullReferenceException

// Check before you dereference:
string? name = null;
if (name is not null)
{
    Console.WriteLine($"Name has {name.Length} characters.");
}
else
{
    Console.WriteLine("Name has no value.");
}
// Output: Name has no value.

Język C# udostępnia trzy uzupełniające narzędzia do pisania kodu bezpiecznego dla wartości null:

  • Typy wartości dopuszczające null: pozwalają typowi wartości, takiemu jak lub , również przyjmować wartość
  • Typy referencyjne dopuszczane do wartości null: pozwól kompilatorowi śledzić, czy odwołanie może być null
  • Operatory null: zapewniają zwięzły dostęp bezpieczny dla wartości null i logikę awaryjną.

Typy wartości dopuszczające wartość null

Typy wartości, takie jak int, double i bool, nie mogą domyślnie przechowywać null. Dodaj ? do nazwy typu, aby utworzyć typ wartości dopuszczający 'null', który zawiera wartość lub null:

int? score = null;
Console.WriteLine(score.HasValue);               // False

score = 95;
Console.WriteLine(score.HasValue);               // True
Console.WriteLine(score.GetValueOrDefault());    // 95

int? missing = null;
Console.WriteLine(missing.GetValueOrDefault(-1)); // -1

Typy wartości dopuszczalnych wartości są przydatne, gdy podstawowy typ wartości musi reprezentować wartość "brak danych". Typowe scenariusze obejmują kolumny bazy danych, które mogą być nieobecne, opcjonalne ustawienia konfiguracji i odczyty czujników, które nie zostały jeszcze przechwycone.

Aby uzyskać pełne pokrycie tematu deklaracji, sprawdzania i konwersji, zobacz Nullable value types.

Referencyjne typy nullowalne

Typy odwołań, takie jak string, tablice i instancje klas, mogą przechowywać null w czasie wykonywania. Typy referencyjne dopuszczające wartość null to funkcja kompilatora, która jawnie określa zamiar użycia wartości null i przechwytuje błędy w czasie kompilacji.

Używając adnotacji ? , deklarujesz intencję:

  • string? — to odwołanie może być null; kompilator ostrzega, jeśli odwołujesz się do niego bez wcześniejszego sprawdzenia
  • string— to odwołanie nie powinno być null; kompilator ostrzega, jeśli go przypiszesz null
// string?  means this reference might be null
// string   means this reference should not be null
string? nullableName = null;
string  nonNullName  = "Alice";

// ?. safely accesses a member when the reference might be null
string display = nullableName?.ToUpper() ?? "(no name)";
Console.WriteLine(display);         // (no name)

display = nonNullName.ToUpper();    // safe: nonNullName is never null
Console.WriteLine(display);         // ALICE

Wszystkie projekty .NET, które tworzone są przez nowoczesne szablony zestawu SDK, domyślnie umożliwiają korzystanie z typów odwołań dopuszczających wartość null. Aby uzyskać pełne wskazówki dotyczące włączania i dodawania adnotacji, zobacz Typy referencyjne dopuszczające wartość Null.

Operatory o wartości null

Język C# zawiera kilka operatorów, które umożliwiają pisanie kodu odpornego na wartość null bez konieczności stosowania ręcznych zabezpieczeń przed if wartością null wszędzie.

Operator Name Purpose
?. Dostęp warunkowy o wartości null Dostęp do elementu członkowskiego tylko wtedy, gdy obiekt ma wartość inną niż null
?[] Dostęp indeksatora warunkowego o wartości null Uzyskiwanie dostępu do elementu tylko wtedy, gdy kolekcja ma wartość inną niż null
?? Operator łączenia z zerowaniem Zwraca wartość rezerwową, gdy wyrażenie jest null
??= Przypisanie łączenia wartości null Przypisz tylko wtedy, gdy zmienna jest null
is null / is not null Wzorzec o wartości null Preferowany test zerowy
string? city = GetCity();

// ?. — access a member only when non-null
int? len = city?.Length;

// ?? — substitute a default when null
string display = city ?? "unknown";

// is null — preferred null test
if (city is null)
{
    Console.WriteLine("No city provided.");
}
else
{
    Console.WriteLine($"{display} ({len} chars)");
}
// Output: No city provided.

Aby uzyskać szczegółowe przykłady poszczególnych operatorów, zobacz Operatory null.

Typy wartości dopuszczalnych do przyjmowania wartości null i typy referencyjne dopuszczalne do wartości null służą różnym celom

Typy wartości dopuszczanych do wartości null i typy referencyjne dopuszczane do wartości null nie są alternatywami. Rozwiązują różne problemy:

  • Użyj T? dla typu wartości, który musi reprezentować brak wartości. Na przykład użyj int? dla opcjonalnej kolumny bazy danych lub DateTime? dla wydarzenia, które jeszcze nie zostało zaplanowane.
  • Użyj string? i innych adnotacji referencyjnych dopuszczających wartość null, aby udokumentować, że odwołanie może mieć wartość null, dzięki czemu kompilator może wyświetlić ostrzeżenie przed wystąpieniem NullReferenceException podczas wykonania.

Razem te funkcje i operatory wartości null zapewniają kompletny zestaw narzędzi do pisania kodu C# bezpiecznego dla wartości null.