07/04/2004
For næsten enhver C# udvikler er synet af en NullReferenceException en velkendt, men altid uvelkommen, oplevelse. Det er en af de mest almindelige fejl under kørsel, og den opstår, når vi forsøger at tilgå et medlem – en egenskab, et felt eller en metode – på en variabel, der er null. Gennem årene har vi udviklet defensive strategier for at bekæmpe dette monster: endeløse kæder af if (obj != null), som kan gøre koden oppustet og svær at læse. Men med introduktionen af C# 6.0 fik vi et elegant og kraftfuldt værktøj i vores arsenal: den null-konditionelle operatør, ofte kaldet sikker navigation. Dette er ikke bare en lille syntaktisk forbedring; det er en fundamental ændring i, hvordan vi skriver sikker og koncis kode.

Hvad er den Null-Konditionelle Operatør?
Den null-konditionelle operatør, også kendt som sikker navigation, findes i to former: medlemsadgang (?.) og indeksadgang (?[]). Dens primære formål er enkelt: at udføre en medlems- eller elementadgangsoperation kun hvis objektet eller samlingen ikke er null. Hvis objektet er null, stopper operationen med det samme og returnerer null, i stedet for at kaste en NullReferenceException.
Lad os se på et klassisk eksempel. Før C# 6.0 ville du måske skrive følgende kode for at få antallet af elementer i en liste, der potentielt kunne være null:
List<string> navne = HentNavneFraDatabase(); int antal = 0; if (navne != null) { antal = navne.Count; } Med den null-konditionelle operatør kan hele denne blok reduceres til en enkelt, letlæselig linje:
List<string> navne = HentNavneFraDatabase(); int? antal = navne?.Count; Læg mærke til to vigtige ting her. For det første brugen af ?. før Count. Dette instruerer compileren: "Hvis navne ikke er null, giv mig værdien af Count. Hvis navne er null, så stop og giv mig null." For det andet er typen af variablen antal nu int? (en nullable int). Dette er nødvendigt, fordi udtrykket navne?.Count kan resultere i enten en integer (hvis listen eksisterer) eller null.
Fordelene der Transformer din Kode
At indføre sikker navigation i din daglige kodning giver en række markante fordele, der går ud over blot at undgå exceptions.
1. Dramatisk Forbedret Læselighed i Kædede Kald
Den virkelige styrke ved operatøren viser sig, når man arbejder med dybt indlejrede objekter. Forestil dig en datamodel for et hospitalssystem, hvor du skal finde navnet på medicinen fra en patients seneste recept.
Den gamle metode ville kræve en række indlejrede null-tjek:
string medicinNavn = "Ikke tilgængelig"; if (patient != null) { if (patient.Journal != null) { if (patient.Journal.SenesteRecept != null) { medicinNavn = patient.Journal.SenesteRecept.MedicinNavn; } } } Denne "pyramide af dom" er både grim og svær at vedligeholde. Med sikker navigation bliver det en elegant enkeltlinje:
string medicinNavn = patient?.Journal?.SenesteRecept?.MedicinNavn; Denne linje læses næsten som en almindelig sætning: "Prøv at få MedicinNavn fra SenesteRecept i Journalen for denne patient." Hvis en hvilken som helst del af kæden (patient, Journal, eller SenesteRecept) er null, vil hele udtrykket elegant returnere null uden drama.
2. Sikker Håndtering af Events
Et andet ekstremt nyttigt anvendelsesområde er ved udløsning af events, især i mønstre som INotifyPropertyChanged. Den traditionelle, trådsikre måde at udløse en event på er at kopiere den til en lokal variabel for at undgå en race condition, hvor en abonnent afmelder sig mellem null-tjekket og kaldet.
protected void OnPropertyChanged(string name) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } Selvom dette er korrekt, er det stadig en smule omstændeligt. Den null-konditionelle operatør giver os en meget mere koncis måde at gøre det på ved at bruge Invoke() metoden på delegaten:
protected void OnPropertyChanged(string name) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } Dette er ikke kun kortere, det er også trådsikkert og er den anbefalede metode i moderne C# 6.0 og nyere versioner.
3. Kombination med Null-Coalescing Operatøren
Sikker navigation spiller perfekt sammen med null-coalescing operatøren (??). Mens ?. giver dig null, hvis noget i kæden er null, giver ?? dig mulighed for at angive en standardværdi i netop det tilfælde. Dette er utroligt kraftfuldt.
// Få patientens postnummer, eller returner "Ukendt" hvis enten patienten eller adressen er null. string postnummer = patient?.Adresse?.Postnummer ?? "Ukendt"; // Få antallet af recepter, eller 0 hvis journalen er null. int antalRecepter = patient?.Journal?.Recepter?.Count ?? 0; Denne kombination eliminerer behovet for efterfølgende if-sætninger for at tildele standardværdier og gør intentionen med koden krystalklar.

Sammenligning: Før og Efter
For at illustrere effekten yderligere, er her en tabel, der sammenligner almindelige scenarier med og uden den null-konditionelle operatør.
| Scenarie | Traditionel Metode (Før C# 6.0) | Moderne Metode (Med ?.) |
|---|---|---|
| Adgang til indlejret egenskab | | |
| Kald af metode | | |
| Adgang til array/liste element | | |
| Tildeling af standardværdi | | |
Vigtige Overvejelser
Selvom operatøren er fantastisk, er der et par ting, man skal være opmærksom på. For det første er det vigtigt at huske, at brugen af ?. kan ændre returtypen til en nullable type. Dette er en central del af sikkerheden, men kræver, at din efterfølgende kode kan håndtere en potentiel null-værdi.
For det andet, mens det er fristende at lave lange kæder, bør man overveje, om en meget lang kæde (f.eks. a?.b?.c?.d?.e) er et tegn på en overtrædelse af designprincipper som Loven om Demeter. Dette princip foreslår, at et objekt kun bør kommunikere med sine nærmeste "venner" og ikke kende til den indre struktur af andre objekter. Nogle gange kan en lang kæde indikere, at det ville være bedre at have en metode på det yderste objekt, der returnerer den ønskede værdi, og dermed skjuler den interne kompleksitet.
Ofte Stillede Spørgsmål (FAQ)
Hvornår blev den null-konditionelle operatør introduceret?
Operatøren blev introduceret i C# 6.0, som blev udgivet sammen med Visual Studio 2015. Den er en standardfunktion i alle moderne .NET-versioner.
Hvad er forskellen mellem `?.` og `??`?
De bruges ofte sammen, men har forskellige formål. ?. (null-konditionel) er til for at tilgå medlemmer på en sikker måde og returnerer null, hvis objektet er null. ?? (null-coalescing) er til for at levere en standardværdi, hvis udtrykket til venstre for den evalueres til null.
Nej, det er netop det, den er designet til at undgå. Hvis patient er null, stopper evalueringen øjeblikkeligt, og hele udtrykket returnerer null. Der kastes ingen exception.
Hvad sker der, hvis jeg bruger `?.` på en værdi-type (struct), som f.eks. `int`?
Hvis du har en egenskab af en værdi-type, som f.eks. en int Alder, vil udtrykket person?.Alder resultere i en nullable type, altså int?. Dette sikrer, at resultatet kan repræsentere null-tilfældet.
Konklusion
Den null-konditionelle operatør er mere end bare syntaktisk sukker. Det er et fundamentalt værktøj i den moderne C#-udviklers værktøjskasse, der fremmer skrivningen af mere robust, pålidelig og udtryksfuld kode. Ved at eliminere store mængder af defensiv, repetitiv null-tjekkende kode, giver den os mulighed for at fokusere på den forretningslogik, der rent faktisk skaber værdi. Hvis du endnu ikke har taget ?. og ?[] til dig i din daglige kodning, er tiden inde. Det vil gøre din kode renere, dit liv lettere og dine applikationer mere stabile.
Hvis du vil læse andre artikler, der ligner Undgå NullReferenceException med C# Sikker Navigation, kan du besøge kategorien Sundhed.
