Typy referencyjne dopuszczane do wartości null (odwołanie w C#)

Uwaga

W tym artykule opisano typy referencyjne dopuszczane do wartości null. Można również zadeklarować typy wartości dopuszczanych do wartości null.

Typy odwołań dopuszczające wartość null są dostępne w kodzie, który zdecydował się na kontekst obsługujący wartość null. Typy referencyjne dopuszczające wartość null, ostrzeżenia analizy statycznej o wartości null i operator forgiving o wartości null są opcjonalnymi funkcjami językowymi. Wszystkie są domyślnie wyłączone. Kontekst dopuszczalny do wartości null jest kontrolowany na poziomie projektu przy użyciu ustawień kompilacji lub w kodzie przy użyciu pragmas.

Ważne

Wszystkie szablony projektów rozpoczynające się od platformy .NET 6 (C# 10) umożliwiają kontekst dopuszczalny do wartości null dla projektu. Projekty utworzone przy użyciu wcześniejszych szablonów nie zawierają tego elementu, a te funkcje są wyłączone, chyba że włączysz je w pliku projektu lub użyj pragmas.

W kontekście obsługującym wartość null:

  • Zmienna typu T odwołania musi zostać zainicjowana z wartością inną niż null i nigdy nie może być przypisana wartość, która może mieć nullwartość .
  • Zmienna typu T? odwołania może zostać zainicjowana z przypisanym null lub przypisanym nullelementem , ale jest wymagana do sprawdzenia przed null odwołaniem.
  • Zmienna typu T? jest uważana za inną m niż null, gdy stosujesz operator forgiving o wartości null, jak w elemm!.

Różnice między typem odwołania nienależącym do wartości null a typem TT? odwołania dopuszczającym wartość null są wymuszane przez interpretację powyższych reguł przez kompilator. Zmienna typu T i zmienna typu T? są reprezentowane przez ten sam typ platformy .NET. W poniższym przykładzie zadeklarowany jest ciąg bez wartości null i ciąg dopuszczający wartość null, a następnie używa operatora forgiving o wartości null do przypisania wartości do ciągu, który nie może mieć wartości null:

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

Zmienne notNull i nullable są reprezentowane przez String typ. Ponieważ typy niezwiązane z wartościami null i dopuszczają wartość null są przechowywane jako ten sam typ, istnieje kilka lokalizacji, w których używanie typu odwołania dopuszczanego do wartości null nie jest dozwolone. Ogólnie rzecz biorąc, nie można użyć typu odwołania dopuszczanego do wartości null jako klasy bazowej ani zaimplementowanego interfejsu. Nie można użyć typu odwołania dopuszczanego do wartości null w żadnym wyrażeniu tworzenia obiektu ani testowania typu. Typ odwołania dopuszczanego do wartości null nie może być typem wyrażenia dostępu do składowej. W poniższych przykładach przedstawiono te konstrukcje:

public MyClass : System.Object? // not allowed
{
}

var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
    if (thing is string? nullableString) // not allowed
        Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
    Console.WriteLine("error");
}

Odwołania dopuszczane do wartości null i analiza statyczna

Przykłady w poprzedniej sekcji ilustrują charakter typów odwołań dopuszczanych do wartości null. Typy referencyjne dopuszczane do wartości null nie są nowymi typami klas, ale raczej adnotacjami w istniejących typach referencyjnych. Kompilator używa tych adnotacji, aby ułatwić znalezienie potencjalnych błędów odwołania o wartości null w kodzie. Nie ma różnicy w czasie wykonywania między typem odwołania bez wartości null a typem odwołania dopuszczanym do wartości null. Kompilator nie dodaje żadnego sprawdzania środowiska uruchomieniowego pod kątem typów odwołań bez wartości null. Korzyści są w analizie czasu kompilacji. Kompilator generuje ostrzeżenia, które ułatwiają znajdowanie i naprawianie potencjalnych błędów null w kodzie. Zadeklarujesz intencję, a kompilator ostrzega Cię, gdy kod narusza ten zamiar.

W kontekście z obsługą wartości null kompilator wykonuje analizę statyczną zmiennych dowolnego typu odwołania, zarówno dopuszczaną do wartości null, jak i niepustą. Kompilator śledzi stan null każdej zmiennej referencyjnej jako nie null lub być może null. Domyślny stan odwołania niezwiązanego z wartością null nie jest zerowy. Domyślnym stanem odwołania dopuszczanego do wartości null jest być może wartość null.

Typy odwołań bez wartości null powinny być zawsze bezpieczne do wyłudzenia, ponieważ ich stan null nie ma wartości null. Aby wymusić daną regułę, kompilator wystawia ostrzeżenia, jeśli typ odwołania niezwiązany z wartością null nie jest inicjowany do wartości innej niż null. Zmienne lokalne muszą być przypisane, gdzie są deklarowane. Każde pole musi mieć przypisaną wartość niepustą w inicjatorze pola lub każdym konstruktorze. Kompilator zgłasza ostrzeżenia, gdy odwołanie niezwiązane z wartością null jest przypisane do odwołania, którego stan może ma wartość null. Ogólnie rzecz biorąc, odwołanie niezwiązane z wartością null nie ma wartości null i nie są wystawiane żadne ostrzeżenia, gdy te zmienne są wyłuszczane.

Uwaga

Jeśli przypiszesz wyrażenie może mieć wartość null do typu odwołania innego niż null, kompilator generuje ostrzeżenie. Kompilator generuje następnie ostrzeżenia dla tej zmiennej, dopóki nie zostanie przypisane do wyrażenia innego niż null .

Typy odwołań dopuszczane do wartości null mogą być inicjowane lub przypisywane do programu null. W związku z tym analiza statyczna musi określić, że zmienna nie ma wartości null , zanim zostanie wyłuszona. Jeśli odwołanie dopuszczające wartość null jest określane jako możliwe do wartości null, przypisanie do zmiennej referencyjnej bez wartości null spowoduje wygenerowanie ostrzeżenia kompilatora. W poniższej klasie przedstawiono przykłady tych ostrzeżeń:

public class ProductDescription
{
    private string shortDescription;
    private string? detailedDescription;

    public ProductDescription() // Warning! shortDescription not initialized.
    {
    }

    public ProductDescription(string productDescription) =>
        this.shortDescription = productDescription;

    public void SetDescriptions(string productDescription, string? details=null)
    {
        shortDescription = productDescription;
        detailedDescription = details;
    }

    public string GetDescription()
    {
        if (detailedDescription.Length == 0) // Warning! dereference possible null
        {
            return shortDescription;
        }
        else
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
    }

    public string FullDescription()
    {
        if (detailedDescription == null)
        {
            return shortDescription;
        }
        else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
        return shortDescription;
    }
}

Poniższy fragment kodu pokazuje, gdzie kompilator emituje ostrzeżenia podczas korzystania z tej klasy:

string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.

string description = "widget";
var item = new ProductDescription(description);

item.SetDescriptions(description, "These widgets will do everything.");

W poprzednich przykładach pokazano, jak analiza statyczna kompilatora określa stan null zmiennych referencyjnych. Kompilator stosuje reguły języka do sprawdzania wartości null i przypisań w celu informowania o analizie. Kompilator nie może podjąć założeń dotyczących semantyki metod lub właściwości. Jeśli wywołasz metody, które wykonują kontrole wartości null, kompilator nie może wiedzieć, że te metody wpływają na stan null zmiennej. Istnieją atrybuty, które można dodać do interfejsów API, aby poinformować kompilator o semantyce argumentów i zwracanych wartości. Te atrybuty zostały zastosowane do wielu typowych interfejsów API w bibliotekach platformy .NET Core. Na przykład IsNullOrEmpty został zaktualizowany, a kompilator poprawnie interpretuje tę metodę jako sprawdzanie wartości null. Aby uzyskać więcej informacji na temat atrybutów, które mają zastosowanie do analizy statycznej stanu null, zobacz artykuł dotyczący atrybutów dopuszczających wartość null.

Ustawianie kontekstu dopuszczalnego do wartości null

Istnieją dwa sposoby kontrolowania kontekstu dopuszczanego do wartości null. Na poziomie projektu można dodać <Nullable>enable</Nullable> ustawienie projektu. W jednym pliku źródłowym języka C# można dodać #nullable enable pragma, aby włączyć kontekst dopuszczalny do wartości null. Zobacz artykuł dotyczący ustawiania strategii dopuszczanej do wartości null. Przed platformą .NET 6 nowe projekty używają wartości domyślnej <Nullable>disable</Nullable>. Począwszy od platformy .NET 6, nowe projekty zawierają <Nullable>enable</Nullable> element w pliku projektu.

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz następujące propozycje specyfikacji języka C#:

Zobacz też