Null-verwijzingstypen (C#-verwijzing)

Notitie

Dit artikel bevat informatie over null-verwijzingstypen. U kunt ook null-waardetypen declareren.

Gebruik nullable-verwijzingstypen in code die zich in een context met null-waarde bevinden. Null-referentietypen, de waarschuwingen voor statische analyse null en de operator null-forgiving zijn optionele taalfuncties. Alles is standaard uitgeschakeld. U bepaalt een null-context op projectniveau met behulp van build-instellingen of in code met behulp van pragma's.

De C#-taalreferentiedocumenten de laatst uitgebrachte versie van de C#-taal. Het bevat ook de eerste documentatie voor functies in openbare previews voor de aanstaande taalrelease.

De documentatie identificeert alle functies die voor het eerst zijn geïntroduceerd in de laatste drie versies van de taal of in de huidige openbare previews.

Aanbeveling

Raadpleeg het artikel over de versiegeschiedenis van de C#-taal om te achterhalen wanneer een functie voor het eerst is geïntroduceerd in C#.

Belangrijk

Alle projectsjablonen maken de nullable context voor het project mogelijk. Projecten die zijn gemaakt met eerdere sjablonen bevatten dit element niet en deze functies zijn uitgeschakeld, tenzij u ze inschakelt in het projectbestand of pragma's gebruikt.

In een niet-compatibele context:

  • U moet een variabele van een verwijzingstype T initialiseren met een niet-null-waarde en u kunt nooit een waarde toewijzen die kan zijn null.
  • U kunt een variabele van een verwijzingstype T? initialiseren met null of toewijzen null, maar u moet deze null controleren voordat u deductie ongedaan maakt.
  • Wanneer u de operator null-forgiving toepast op een variabele m van het type T?, zoals in m!, wordt de variabele beschouwd als niet-null.

De compiler dwingt het onderscheid af tussen een niet-nullable verwijzingstype T en een null-verwijzingstype T? met behulp van de voorgaande regels. Een variabele van het type T en een variabele van het type T? zijn hetzelfde .NET-type. In het volgende voorbeeld worden een niet-null-tekenreeks en een null-tekenreeks declareerd en wordt vervolgens de operator null-forgiving gebruikt om een waarde toe te wijzen aan een niet-null-tekenreeks:

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

De variabelen notNull en nullable beide gebruiken het String type. Omdat de niet-nullbare en null-bare typen beide hetzelfde type gebruiken, kunt u geen null-verwijzingstype op verschillende locaties gebruiken. Over het algemeen kunt u geen null-verwijzingstype gebruiken als basisklasse of geïmplementeerde interface. U kunt geen null-verwijzingstype gebruiken in een expressie voor het maken van objecten of het testen van typen. U kunt geen null-verwijzingstype gebruiken als het type lidtoegangsexpressie. In de volgende voorbeelden ziet u de volgende constructies:

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");
}

Null-verwijzingen en statische analyse

In de voorbeelden in de vorige sectie ziet u de aard van null-verwijzingstypen. Null-verwijzingstypen zijn geen nieuwe klassetypen, maar eerder aantekeningen op bestaande referentietypen. De compiler gebruikt deze aantekeningen om potentiële null-verwijzingsfouten in uw code te vinden. Er is geen runtime-verschil tussen een niet-null-verwijzingstype en een null-verwijzingstype. De compiler voegt geen runtimecontrole toe voor niet-nullable referentietypen. De voordelen zijn opgenomen in de analyse van de compilatietijd. De compiler genereert waarschuwingen waarmee u potentiële null-fouten in uw code kunt vinden en oplossen. U declareert uw intentie en de compiler waarschuwt u wanneer uw code die intentie schendt.

Belangrijk

Aantekeningen bij null-verwijzingen veroorzaken geen gedragswijzigingen, maar andere bibliotheken kunnen reflectie gebruiken om ander runtimegedrag te produceren voor null-bare en niet-null-referentietypen. Opmerkelijk genoeg leest Entity Framework Core nullable kenmerken. Een nullable-verwijzing wordt geïnterpreteerd als een optionele waarde en een niet-nullable-verwijzing als een verplichte waarde.

In een context met null-functionaliteit voert de compiler statische analyse uit op variabelen van elk verwijzingstype, zowel nullable als niet-nullable. De compiler houdt de null-status van elke verwijzingsvariabele bij als niet-null of misschien-null. De standaardstatus van een niet-null-verwijzing is niet null. De standaardstatus van een null-verwijzing is misschien null.

Niet-null-referentietypen moeten altijd veilig zijn om te deductie omdat de null-status niet null is. Om deze regel af te dwingen, geeft de compiler waarschuwingen uit als een niet-nullable verwijzingstype niet is geïnitialiseerd op een niet-null-waarde. U moet lokale variabelen toewijzen waar u ze declareert. Aan elk veld moet een niet-null-waarde worden toegewezen, in een veld-initialisatiefunctie of elke constructor. De compiler geeft waarschuwingen wanneer een niet-null-verwijzing wordt toegewezen aan een verwijzing waarvan de status mogelijk null is. Over het algemeen is een niet-null-verwijzing niet null en worden er geen waarschuwingen gegeven wanneer u deze variabelen deducteert.

Notitie

Als u een mogelijk null-expressie toewijst aan een niet-null-referentietype , genereert de compiler een waarschuwing. De compiler genereert vervolgens waarschuwingen voor die variabele totdat deze is toegewezen aan een not-null-expressie .

U kunt nullable-verwijzingstypen initialiseren of toewijzen null . Statische analyse moet daarom bepalen dat een variabele niet null is voordat deze wordt gededucteerd. Als wordt vastgesteld dat een null-verwijzing mogelijk null is, genereert het toewijzen ervan aan een niet-null-referentievariabele een compilerwaarschuwing. In de volgende klasse ziet u voorbeelden van deze waarschuwingen:

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;
    }
}

In het volgende codefragment ziet u waar de compiler waarschuwingen verzendt bij het gebruik van deze klasse:

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.");

In de voorgaande voorbeelden ziet u hoe de statische analyse van de compiler de null-status van referentievariabelen bepaalt. De compiler past taalregels toe voor null-controles en -toewijzingen om de analyse ervan te informeren. De compiler kan geen veronderstellingen maken over de semantiek van methoden of eigenschappen. Als u methoden aanroept die null-controles uitvoeren, kan de compiler deze methoden niet kennen van invloed zijn op de null-status van een variabele. U kunt kenmerken toevoegen aan uw API's om de compiler te informeren over de semantiek van argumenten en retourwaarden. Veel algemene API's in de .NET-bibliotheken hebben deze kenmerken. De compiler interpreteert IsNullOrEmpty bijvoorbeeld correct als een null-controle. Zie het artikel over null-able kenmerken voor meer informatie over de kenmerken die van toepassing zijn op statische analyse met null-status.

Null-context

De context die null kan worden gebruikt, bepaalt hoe de compiler aantekeningen van null-verwijzingstypen verwerkt en welke waarschuwingen er worden gegenereerd tijdens statische null-statusanalyse. De null-context bevat twee vlaggen: de aantekeningsinstelling en de waarschuwingsinstelling.

Zowel de aantekening als waarschuwing instellingen zijn standaard uitgeschakeld voor bestaande projecten. Vanaf .NET 6 (C# 10) zijn beide vlaggen standaard ingeschakeld voor nieuw projecten. De reden voor twee afzonderlijke vlaggen voor de nulbare context is om het eenvoudiger te maken om grote projecten te migreren die dateren van vóór de introductie van nulbare referentietypen.

Voor kleine projecten kun je nulbare referentietypen inschakelen, waarschuwingen corrigeren en doorgaan. Voor grotere projecten en oplossingen voor meerdere projecten kan dat proces echter een groot aantal waarschuwingen genereren. U kunt pragma's gebruiken om nullable-referentietypes per bestand in te schakelen wanneer u begint met het gebruik van nullable-referentietypes. De nieuwe functies die beschermen tegen het genereren van een System.NullReferenceException bestand, kunnen storend zijn wanneer ze zijn ingeschakeld in een bestaande codebasis:

  • Alle expliciet getypte referentievariabelen worden geïnterpreteerd als niet-nullable verwijzingstypen.
  • De betekenis van de class beperking in generieke types is gewijzigd naar een niet-nulleerbare referentietype.
  • Er worden nieuwe waarschuwingen gegenereerd vanwege deze nieuwe regels.

De nullable-annotatiecontext bepaalt het gedrag van de compiler. Er zijn vier combinaties voor de nullable-context-instellingen:

  • beide uitgeschakeld: De code is nullable-onbewust. Uitschakelen komt overeen met het gedrag voordat null-verwijzingstypen zijn ingeschakeld, behalve dat de nieuwe syntaxis waarschuwingen produceert in plaats van fouten.
    • Null-waarschuwingen zijn uitgeschakeld.
    • Alle referentietypevariabelen zijn null-referentietypen.
    • Het gebruik van het ? achtervoegsel om een nullable verwijzingstype te declareren, produceert een waarschuwing.
    • U kunt de operator null forgiving gebruiken, !maar dit heeft geen effect.
  • beide ingeschakeld: de compiler schakelt alle null-referentieanalyse en alle taalfuncties in.
    • Alle nieuwe null-bare waarschuwingen zijn ingeschakeld.
    • U kunt het ? achtervoegsel gebruiken om een null-verwijzingstype te declareren.
    • Verwijzingstypevariabelen zonder achtervoegsel ? zijn niet-nullbare verwijzingstypen.
    • De null forgiving operator onderdrukt waarschuwingen voor een mogelijke dereferentie van null.
  • waarschuwing ingeschakeld: de compiler voert alle null-analyse uit en geeft waarschuwingen wanneer de code mogelijk dereferentie naar null.
    • Alle nieuwe null-bare waarschuwingen zijn ingeschakeld.
    • Het gebruik van het ? achtervoegsel om een nullable verwijzingstype te declareren, produceert een waarschuwing.
    • Alle referentietypevariabelen mogen null zijn. Leden hebben echter de null-statusnot-null bij de accolade openen van alle methoden, tenzij ze zijn gedeclareerd met het ? achtervoegsel.
    • U kunt de null-forgiving operator ! gebruiken.
  • aantekeningen ingeschakeld: de compiler geeft geen waarschuwingen af wanneer code mogelijk dereferentiert null, of wanneer u een mogelijk-null-expressie toewijst aan een niet-null-variabele.
    • Alle nieuwe nullable-waarschuwingen zijn uitgeschakeld.
    • U kunt het ? achtervoegsel gebruiken om een null-verwijzingstype te declareren.
    • Verwijzingstypevariabelen zonder achtervoegsel ? zijn niet-nullbare verwijzingstypen.
    • U kunt de operator null forgiving gebruiken, !maar dit heeft geen effect.

U kunt de context van de null-aantekening en de context voor null-waarschuwingen voor een project instellen met behulp van het <Nullable> element in uw .csproj-bestand . Dit element configureert hoe de compiler de null-waarde van typen interpreteert en welke waarschuwingen worden verzonden. In de volgende tabel worden de toegestane waarden weergegeven en wordt samengevat in welke contexten ze gebruikt kunnen worden.

Context Dereference-waarschuwingen Toewijzingswaarschuwingen Verwijzingstypen ? achtervoegsel ! bediener
disable Uitgeschakeld Uitgeschakeld Alle zijn optioneel aanpasbaar Hiermee wordt een waarschuwing gegenereerd Heeft geen effect
enable Ingeschakeld Ingeschakeld Niet-nullable tenzij gedeclareerd met ? Verklaart een nullable type Onderdrukt waarschuwingen voor mogelijke null toewijzingen
warnings Ingeschakeld Niet van toepassing Alle zijn nullable, maar leden worden beschouwd als niet-null bij het openen van methoden. Hiermee wordt een waarschuwing gegenereerd Onderdrukt waarschuwingen voor mogelijke null toewijzingen
annotations Uitgeschakeld Uitgeschakeld Niet-nullable tenzij gedeclareerd met ? Verklaart een nullable type Heeft geen effect

Variabelen voor verwijzingstypen in code die zijn gecompileerd in een uitgeschakelde context, zijn null-vergetelbaar. U kunt een null letterlijke waarde of een misschien null-variabele toewijzen aan een variabele die null-vergetelbaar is. De standaardstatus van een nullable-oblivious variabele is echter niet-null.

Kies de instelling die het beste past bij uw project:

  • Kies uitschakelen voor verouderde projecten die u niet wilt bijwerken op basis van diagnostische gegevens of nieuwe functies.
  • Kies waarschuwingen om te bepalen waar uw code System.NullReferenceExceptions kan genereren. U kunt deze waarschuwingen aanpakken voordat u code wijzigt om niet-opnullbare verwijzingstypen in te schakelen.
  • Kies aantekeningen om uw ontwerpintentie uit te drukken voordat u waarschuwingen inschakelt.
  • Kies Inschakelen voor nieuwe projecten en actieve projecten waarvoor u wilt beveiligen tegen null-referentie-uitzonderingen.

voorbeeld van:

<Nullable>enable</Nullable>

U kunt ook instructies gebruiken om dezelfde vlaggen overal in uw broncode in te stellen. Deze instructies zijn het handigst wanneer u een grote codebasis migreert.

  • #nullable enable: hiermee stelt u de aantekening en waarschuwingsvlaggen in op inschakelen.
  • #nullable disable: Hiermee stelt u de annotatie- en waarschuwingsvlaggen in om uit te schakelen.
  • #nullable restore: hiermee herstelt u de aantekeningsvlag en waarschuwingsvlag naar de projectinstellingen.
  • #nullable disable warnings: Hiermee stelt u de waarschuwingsvlag in om uit te schakelen.
  • #nullable enable warnings: Hiermee stelt u de waarschuwingsvlag in om in te schakelen.
  • #nullable restore warnings: hiermee herstelt u de waarschuwingsvlag in de projectinstellingen.
  • #nullable disable annotations: Hiermee stelt u de aantekeningsvlag in om uit te schakelen.
  • #nullable enable annotations: Hiermee stelt u de aantekeningsvlag in om in te schakelen.
  • #nullable restore annotations: hiermee herstelt u de aantekeningsvlag naar de projectinstellingen.

Voor elke regel code kunt u een van de volgende combinaties instellen:

Waarschuwingsvlag Aantekeningsvlag Gebruik
standaardproject standaardproject Verstek
enable disable Analysewaarschuwingen oplossen
enable standaardproject Analysewaarschuwingen oplossen
standaardproject enable Typeaantekeningen toevoegen
enable enable Code is al gemigreerd
disable enable Aantekeningen toevoegen aan code voordat waarschuwingen worden opgelost
disable disable Verouderde code toevoegen aan gemigreerd project
standaardproject disable Zelden
disable standaardproject Zelden

Deze negen combinaties bieden u nauwkeurige controle over de diagnostische gegevens die de compiler verzendt voor uw code. U kunt meer functies inschakelen in elk gebied dat u bijwerkt, zonder dat u meer waarschuwingen ziet die u nog niet kunt adresseren.

Belangrijk

De globale nullable context is niet van toepassing op gegenereerde codebestanden. Onder een van beide strategieën wordt de null-context uitgeschakeld voor elk bronbestand dat is gemarkeerd als gegenereerd. Deze voorwaarde betekent dat de compiler geen aantekeningen maakt bij API's in gegenereerde bestanden. De compiler produceert geen null-waarschuwingen voor gegenereerde bestanden. Een bestand is gemarkeerd als gegenereerd op een van de volgende vier manieren:

  1. Geef generated_code = true in de .editorconfig een sectie op die van toepassing is op dat bestand.
  2. Plaats <auto-generated> of <auto-generated/> in een opmerking boven aan het bestand. Het kan op elke regel in die opmerking staan, maar het opmerkingenblok moet het eerste element in het bestand zijn.
  3. Start de bestandsnaam met TemporaryGeneratedFile_
  4. Beëindig de bestandsnaam met .designer.cs, .generated.cs, .g.cs of .g.i.cs.

Generatoren kunnen zich aanmelden met behulp van de #nullable preprocessorrichtlijn.

Standaard zijn null-aantekeningen en waarschuwingsvlaggen uitgeschakeld. Dit betekent dat uw bestaande code zonder wijzigingen wordt gecompileerd en dat er geen nieuwe waarschuwingen worden gegenereerd. Vanaf .NET 6 bevatten nieuwe projecten het element <Nullable>enable</Nullable> in alle projectsjablonen, waarbij u deze vlaggen instelt op enabled.

Deze opties bieden twee verschillende strategieën voor het bijwerken van een bestaande codebasis voor het gebruik van null-referentietypen.

De null-context instellen

U kunt de context waarop null kan worden uitgevoerd op twee manieren beheren. Voeg op projectniveau de <Nullable>enable</Nullable> projectinstelling toe. Voeg in één C#-bronbestand de #nullable enable pragma toe om de null-context in te schakelen. Zie het instellen van een nullable strategie voor meer informatie. Voordat .NET 6, gebruiken nieuwe projecten de standaardinstelling, <Nullable>disable</Nullable>. Vanaf .NET 6 bevatten nieuwe projecten het <Nullable>enable</Nullable> element in het projectbestand.

Generieken

Wanneer u een typeparameter gebruikt, Tals null-equivalent, T?bepaalt het werkelijke typeargument hoe het ? wordt geïnterpreteerd. Houd rekening met de volgende algemene declaratie:

public class Box<T>
{
    public T Contents { get; set; }
}

Omdat een typeparameter kan staan voor een verwijzingstype of een waardetype, hangt de betekenis T? ervan af van welk typeargument de aanroeper levert. In de volgende regels wordt beschreven wat T? er wordt omgezet wanneer T er geen beperkingen gelden:

  • Het typeargument is een niet-null-verwijzingstype. Voor Box<string>, T is string en T? is string?- het bijbehorende nullable verwijzingstype.
  • Het typeargument is een waardetype. Voor Box<int>, T is int en T? is ook int- hetzelfde waardetype. De aantekening heeft geen effect op waardetypen, tenzij de typeparameter de struct beperking heeft, in dat geval T? betekent Nullable<T> (int?).
  • Het typeargument is al nullable. Voor Box<string?>, T is string? en T? is nog steeds string?. U krijgt geen 'dubbel nullable' type.

Beperkingen beperken welke typeargumenten zijn toegestaan. Ze laten de compiler ook redeneren over hoe T kan worden gebruikt:

  • where T : class vereist een niet-null-verwijzingstype. Box<string> is toegestaan; Box<string?> produceert een waarschuwing.
  • where T : class? staat een nullable of een niet-nullable verwijzingstype toe. Beide Box<string> en Box<string?> zijn toegestaan.
  • where T : struct vereist een niet-null-waardetype. Box<int> is toegestaan; Box<int?> Niet. Met deze beperking, T? binnen de algemene middelen Nullable<T>, voor Box<int>, T? is int?.
  • where T : notnull vereist een niet-null-verwijzing of waardetype. Box<string> en Box<int> zijn toegestaan; Box<string?> produceert een waarschuwing.
  • where T : BaseType vereist een niet-null-verwijzingstype dat is afgeleid van BaseType. Voeg ? (where T : BaseType?) toe om ook null-bare afgeleide typen toe te staan.

De beperkingen helpen de compilerreden over hoe een algemene typeparameter wordt gebruikt:

public static T? FirstOrDefault<T>(IEnumerable<T> source)
{
    foreach (T item in source)
    {
        return item;
    }
    return default;
}

public static void RequireNotNull<T>(T value) where T : notnull
{
    ArgumentNullException.ThrowIfNull(value);
}

public static void Generics()
{
    string? first = FirstOrDefault<string>([]);
    Console.WriteLine(first ?? "<empty>");

    RequireNotNull("not null");
}

C#-taalspecificatie

Zie de sectie Nullable reference types van de C#-taalspecificatie voor meer informatie.

Zie ook