Intentie uitdrukken

Voltooid

In de vorige les hebt u geleerd hoe de C#-compiler statische analyse kan uitvoeren om u te beschermen NullReferenceException. U hebt ook geleerd hoe u een null-context inschakelt. In deze les leert u meer over het expliciet uitdrukken van uw intentie in een null-context.

Variabelen declareren

Als een null-context is ingeschakeld, hebt u meer inzicht in hoe de compiler uw code ziet. U kunt reageren op de waarschuwingen die zijn gegenereerd op basis van een context met null-functionaliteit. In dat opzicht definieert u expliciet uw intenties. Laten we bijvoorbeeld doorgaan met het onderzoeken van de FooBar code en het onderzoeken van de declaratie en toewijzing:

// Define as nullable
FooBar? fooBar = null;

Noteer de ? toegevoegde aan FooBar. Hiermee wordt aan de compiler aangegeven dat u expliciet van plan bent fooBar nullable te zijn. Als u niet van plan fooBar bent nullable te zijn, maar u toch de waarschuwing wilt vermijden, kunt u het volgende overwegen:

// Define as non-nullable, but tell compiler to ignore warning
// Same as FooBar fooBar = default!;
FooBar fooBar = null!;

In dit voorbeeld wordt de operator null-forgiving (!) toegevoegd aan null, waarmee de compiler wordt geïnstrueerd dat u deze variabele expliciet initialiseert als null. De compiler geeft geen waarschuwingen over deze verwijzing die null is.

Een goede gewoonte is om uw niet-null-variabelen niet-waardennull toe te wijzen wanneer ze indien mogelijk worden gedeclareerd:

// Define as non-nullable, assign using 'new' keyword
FooBar fooBar = new(Id: 1, Name: "Foo");

Operators

Zoals besproken in de vorige les, definieert C# verschillende operators om uw intentie uit te drukken rond null-referentietypen.

Null-forgiving-operator (!)

U hebt kennisgemaakt met de operator null-forgiving (!) in de vorige sectie. Hiermee wordt aan de compiler aangegeven dat de cs8600-waarschuwing moet worden genegeerd. Dit is een manier om de compiler te vertellen dat u weet wat u doet, maar het komt met het voorbehoud dat u eigenlijk moet weten wat u doet.

Wanneer u niet-nullable typen initialiseert terwijl een null-context is ingeschakeld, moet u de compiler mogelijk expliciet om vergeving vragen. Denk bijvoorbeeld aan de volgende code:

#nullable enable

using System.Collections.Generic;

var fooList = new List<FooBar>
{
    new(Id: 1, Name: "Foo"),
    new(Id: 2, Name: "Bar")
};

FooBar fooBar = fooList.Find(f => f.Name == "Bar");

// The FooBar type definition for example.
record FooBar(int Id, string Name);

In het voorgaande voorbeeld FooBar fooBar = fooList.Find(f => f.Name == "Bar"); wordt een CS8600-waarschuwing gegenereerd, omdat Find deze mogelijk wordt geretourneerd null. Dit kan null worden toegewezen aan fooBar, wat in deze context niet nullable is. In dit gewende voorbeeld weten we echter dat dat Find nooit zal terugkeren null als geschreven. U kunt deze intentie naar de compiler uitdrukken met de operator null-forgiving:

FooBar fooBar = fooList.Find(f => f.Name == "Bar")!;

Noteer de ! aan het einde van fooList.Find(f => f.Name == "Bar"). Dit vertelt de compiler dat u weet dat het object dat door de Find methode wordt geretourneerd, mogelijk is nullen dat het geen probleem is.

U kunt de operator null-forgiving ook toepassen op een object inline vóór een methodeaanroep of evaluatie van eigenschappen. Kijk eens naar een ander voorbeeld:

List<FooBar>? fooList = FooListFactory.GetFooList();

// Declare variable and assign it as null.
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!; // generates warning

static class FooListFactory
{
    public static List<FooBar>? GetFooList() =>
        new List<FooBar>
        {
            new(Id: 1, Name: "Foo"),
            new(Id: 2, Name: "Bar")
        };
}

// The FooBar type definition for example.
record FooBar(int Id, string Name);

In het voorgaande voorbeeld:

  • GetFooList is een statische methode die een null-type retourneert, List<FooBar>?.
  • fooList wordt de waarde toegewezen die wordt geretourneerd door GetFooList.
  • De compiler genereert een waarschuwing omdat fooList.Find(f => f.Name == "Bar"); de waarde die aan deze compiler is toegewezen fooList , mogelijk is null.
  • Ervan uitgaande fooList dat dit niet nullhet resultaat is, Find kan worden geretourneerd null, maar we weten dat dat niet het gaat, dus de operator null-forgiving wordt toegepast.

U kunt de operator null-forgiving toepassen om de waarschuwing uit te fooList schakelen:

FooBar fooBar = fooList!.Find(f => f.Name == "Bar")!;

Notitie

U moet de operator null-forgiving zorgvuldig gebruiken. Als u deze gewoon gebruikt om een waarschuwing te sluiten, betekent dit dat u de compiler niet vertelt dat u mogelijke null-mishaps kunt detecteren. Gebruik het spaarzaam en alleen wanneer u zeker bent.

Voor meer informatie, naslag ! (null-forgiving)-operator (C#-verwijzing).

Operator Null-coalescing (??)

Wanneer u met null-typen werkt, moet u mogelijk evalueren of deze momenteel null zijn en bepaalde acties uitvoeren. Wanneer bijvoorbeeld een null-type is toegewezen null of niet-geïnitialiseerd is, moet u deze mogelijk een niet-null-waarde toewijzen. Hier is de operator null-coalescing (??) handig.

Kijk een naar het volgende voorbeeld:

public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
    salesTax ??= DefaultStateSalesTax.Value;

    // Safely use salesTax object.
}

In de voorgaande C#-code:

  • De salesTax parameter wordt gedefinieerd als een null-waarde IStateSalesTax.
  • Binnen de hoofdtekst van de methode wordt de salesTax voorwaardelijk toegewezen met behulp van de operator null-coalescing.
    • Dit zorgt ervoor dat als salesTax deze is doorgegeven, null een waarde heeft.

Tip

Dit is functioneel gelijk aan de volgende C#-code:

public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
    if (salesTax is null)
    {
        salesTax = DefaultStateSalesTax.Value;
    }

    // Safely use salesTax object.
}

Hier volgt een voorbeeld van een andere veelvoorkomende C#-idioom waarbij de operator null-coalescing nuttig kan zijn:

public sealed class Wrapper<T> where T : new()
{
    private T _source;

    // If given a source, wrap it. Otherwise, wrap a new source:
    public Wrapper(T source = null) => _source = source ?? new T();
}

De voorgaande C#-code:

  • Definieert een algemene wrapper-klasse, waarbij de parameter voor het algemene type is beperkt tot new().
  • De constructor accepteert een T source parameter die standaard is ingesteld nullop .
  • De wrapped _source wordt voorwaardelijk geïnitialiseerd naar een new T().

Bekijk voor meer informatie ?? en ?? = operatoren (C#-verwijzing).

Operator null-voorwaardelijk (?.)

Wanneer u werkt met null-typen, moet u mogelijk voorwaardelijke acties uitvoeren op basis van de status van een null object. Bijvoorbeeld: in de vorige eenheid werd de FooBar record gebruikt om te demonstreren NullReferenceException door deferencing null. Dit werd veroorzaakt toen het ToString werd aangeroepen. Bekijk hetzelfde voorbeeld, maar pas nu de null-voorwaardelijke operator toe:

using System;

// Declare variable and assign it as null.
FooBar fooBar = null;

// Conditionally dereference variable.
var str = fooBar?.ToString();
Console.Write(str);

// The FooBar type definition.
record FooBar(int Id, string Name);

De voorgaande C#-code:

  • Voorwaardelijk deducties fooBar, waarbij het resultaat van ToString de str variabele wordt toegewezen.
    • De str variabele is van het type string? (nullable string).
  • De waarde van str de standaarduitvoer wordt weggeschreven. Dit is niets.
  • Bellen Console.Write(null) is geldig, dus er zijn geen waarschuwingen.
  • U krijgt een waarschuwing als u zou bellen Console.Write(str.Length) , omdat u mogelijk null zou deductie ongedaan maken.

Tip

Dit is functioneel gelijk aan de volgende C#-code:

using System;

// Declare variable and assign it as null.
FooBar fooBar = null;

// Conditionally dereference variable.
string str = (fooBar is not null) ? fooBar.ToString() : default;
Console.Write(str);

// The FooBar type definition.
record FooBar(int Id, string Name);

U kunt operator combineren om uw intentie verder uit te drukken. U kunt de operatoren ?? bijvoorbeeld koppelen?.:

FooBar fooBar = null;
var str = fooBar?.ToString() ?? "unknown";
Console.Write(str); // output: unknown

Raadpleeg de operators ?. en ?[] (null-conditional) voor meer informatie.

Samenvatting

In deze les hebt u geleerd over het uitdrukken van uw intentie voor null-baarheid in code. In de volgende les past u toe wat u hebt geleerd op een bestaand project.

Kennis testen

1.

Wat is de default waarde van het string verwijzingstype?

2.

Wat is het verwachte gedrag van de deductie null?

3.

Wat gebeurt er wanneer deze throw null; C#-code wordt uitgevoerd?

4.

Welke instructie is het meest nauwkeurig met betrekking tot null-verwijzingstypen?