C# 9.0 nieuwe features – Pattern Matching (2-4)

Met de komst van C# versie 9.0 zijn er heel veel waardevolle toevoegingen gedaan aan de programmeertaal. In deze Blog serie zullen we inzoomen op een aantal belangrijke nieuwe features. Al eerder behandeld in deze serie is deel 1 “Init Only Setters”, we gaan nu verder met “Pattern Matching”.

Om C# 9.0 te kunnen gebruiken dien je eerst de .NET 5 runtime of SDK te installeren. Ga hiervoor naar https://dotnet.microsoft.com/download. Na installatie zal C# 9.0 de standaard C# versie zijn bij elk nieuw project gebaseerd op .NET 5.

Waarom?

De syntax van ontwikkeltalen is al sinds jaren het onderwerp van discussies tussen ontwikkelaars. Leesbaarheid, efficiëntie en flexibiliteit is slechts een selectie van de tientallen argumenten die jouw mening vormen over het al dan niet geslaagd zijn van een syntax.
Martin Fowler geeft een – zoals we van hem gewend zijn – zeer zinvolle bijdrage in deze discussie met zijn bekende uitspraak:

Any fool can write code that a computer can understand.
Good programmers write code that humans can understand.

Met die uitspraak in het achterhoofd heeft Microsoft een aantal vereenvoudigingen aan de syntax van if en switch doorgevoerd.

Deze onderdelen worden door ons veel gebruikt om complexe condities uit te werken.
Sinds C# 7.0 helpt Pattern Matching ons om de code die we opleveren beter leesbaar en dus minder foutgevoelig te maken. Ook in C# 9.0 zijn er weer enkele nieuwe mogelijkheden toegevoegd.

Hoe?

Logical pattern

Om meer complexe condities goed leesbaar te houden is het logical pattern toegevoegd aan C# 9.0. Hiermee kunnen we gebruik maken van and, or en not in onze statements, en het herhalen van de parameter waarmee vergeleken moet worden kan achterwege worden gelaten.

Dit is een krachtig mechanisme dat kan worden gebruikt in if-statements zoals in bijgaand voorbeeld. De volgorde waarin de controles worden uitgevoerd is volstrekt logisch, maar in sommige gevallen zul je toch aan deze volgorde moeten wennen. Vergeet dus niet om unittests toe te voegen met voldoende testsets om er zeker van te zijn dat je functie in alle gevallen het gewenste gedrag vertoont.

Het Logical pattern komt zelfs beter tot zijn recht wanneer dit wordt gecombineerd in switch expressions, zoals we hierna zullen zien bij het Relational Pattern.

Relational pattern

In C# 8.0 zijn switch expressions toegevoegd. Hiermee was het mogelijk om direct een returnwaarde als resultaat van een aantal voorwaarden te definiëren. De syntax bevatte veel onnodige herhalingen en was daarom nog niet in alle gevallen even leesbaar, zoals het onderstaande voorbeeld illustreert.

blank

In C# 9.0 kunnen we de bovenstaande syntax verder verfijnen door gebruik te maken van het Relational pattern. Hierdoor kunnen we rechtstreeks gebruik maken van relational operators zoals < of >=. Toepassing leidt tot het volgende resultaat:

blank

In deze syntax ligt de nadruk op de logica en het resultaat, en veel minder op het achterhalen van de juiste objecten.
We zien ook dat het gebruik van het Logical Pattern ook daadwerkelijk de leesbaarheid van de code vergroot: in één oogopslag is nu duidelijk dat er een selectie gemaakt wordt voor iemand tussen de 12 en 60.

Dit voorbeeld leent zich daarnaast om de switch expression, die in C# 8.0 al is geïntroduceerd nogmaals onder de aandacht te brengen.
Bij de switch expression staat de variabele vóór het switch keyword, de switch-onderdelen case en : zijn vervangen door de bekende =>. Als laatste is de body is geen statement meer, maar een expressie.
De expression is in het onderstaand voorbeeld uitgewerkt. Hierbij zien we dat we ons niet hoeven te beperken tot property checking, ook Type checking behoort tot de mogelijkheden.

blank

Type pattern

Met het type pattern kun je met een vereenvoudigde syntax conditie checks op type en properties uitvoeren . De nieuwe schrijfwijze in C# 9.0 is compact en overzichtelijk, zoals in onderstaand voorbeeld wordt getoond.

blank

Wanneer de parameter person niet van het type PersonRecord is ontstaat er geen exception maar wordt de evaluatie van het if-statement afgebroken. In het andere geval (person is van het type PersonRecord) wordt het resultaat bepaald op basis van de property Age van person.

Wees je er in dit voorbeeld goed van bewust dat deze code géén NullReferenceException oplevert wanneer de parameter person null is.

Verder?

Veel van onze logica in onze applicaties is vormgegeven door implementaties van if-then-else en switch statements. Na verloop van tijd kunnen deze constructies steeds complexer en onoverzichtelijker worden.
Met de nieuwe mogelijkheden kunnen we deze bestaande code relatief eenvoudig refactoren naar beter leesbare en dus beter onderhoudbare code.

Maar natuurlijk geldt zoals altijd: bedenk of deze complexe constructies gezien kunnen worden als code smell voor het overtreden van belangrijke SOLID principes, zoals het Open/Closed of Single Responsibility principe.
Overweeg in die gevallen vooral ook de toepassing van nette OO oplossingen, zoals het inzetten van encapsulation en de vele design patterns die beschikbaar zijn.

Deel deze pagina via:
berglerC# 9.0 nieuwe features – Pattern Matching (2-4)