C# 9.0 is nu uit!
Het is officieel: C# 9.0 is nu uit! Met elke nieuwe versie van C# streeft Microsoft naar meer duidelijkheid en eenvoud in algemene codeer scenario’s. Hierin is C# 9.0 geen uitzondering. Een specifieke focus bij deze update is het ondersteunen van de beknopte en onveranderlijke representatie van het vormen van data.
Init only setters
Init only setters bieden consistente syntax om leden van een onderwerp te initialiseren. Property initializers maken duidelijk welke waarde welke property plaatst. Het nadeel is dat die properties instelbaar moeten zijn. Door C# 9.0 te gebruiken kun je init accessors in plaats van set accessors voor properties en indexers maken. Developers kunnen de property initializer syntax gebruiken om waardes in creation expressions te zetten, maar die properties zijn readonly wanneer de bouw voltooid is. Init only setters bieden een venster om de staat te veranderen. Dat venster sluit wanneer de bouwfase eindigt. De bouwfase eindigt na alle initialisatie, hierbij horen ook property initializers en with-expressions die voltooid zijn.
Je kunt init only setters in elke type die je schrijft vaststellen. Bijvoorbeeld de volgende struct, deze definieert een weer observatie structuur:
Developers kunnen de property initializer syntax gebruiken om waarden in te stellen terwijl ze de onveranderlijkheid kunnen behouden:
Maar het veranderen van een observatie na het initialiseren geeft een error door het toewijzen aan een init-only property buiten het initialiseren:
Init only setters kunnen bruikbaar zijn om base class properties van afgeleide classes in te stellen. Ze kunnen ook afgeleide properties door helpers in een base class instellen. Positionele records stellen properties door het gebruik van init only setters vast. Die setters worden gebruikt in with-expressions. Je kunt init only setters vaststellen voor elke class of struct die je definieert.
Record types
C# 9.0 introduceert record types, wat een referentie type is die zorgt voor gesynthetiseerde methodes om waarde semantics voor gelijkheid beschikbaar te maken. Records zijn standaard onveranderlijk.
Record types maken het makkelijk om onveranderlijke referentie types te maken in .NET. Vroeger waren .NET types grotendeels geclassificeerd als referentie types (hierbij horen classes en anonieme types) en waarde types (hierbij horen structs en tuples). Terwijl onveranderlijke waarde types aanbevolen zijn, komen er vaak geen errors van veranderlijke waarde types. Variabelen die waarde types zijn houden de waarden, dus veranderingen worden gemaakt naar een kopie van de originele data wanneer waarde types doorgezet worden naar methoden.
Ook zijn er veel voordelen voor het gebruik van onveranderlijke referentie types. Deze voordelen zijn meer uitgesproken in andere programma’s met gedeelde data. Helaas dwong C# je ertoe om meer extra codering te schrijven om een onveranderlijk referentie type te maken. Records bieden een typverklaring voor een onveranderlijk referentie type die gebruik maakt van waarde semantics voor gelijkheid. De gesynthetiseerde methoden voor gelijkheid en hash codes beoordelen twee records als gelijk wanneer hun properties allemaal gelijk zijn. Neem deze definitie:
De record definitie maakt een Person type dat twee readonly properties bevat: FirstName en LastName. De Person type is een referentie type. Wanneer je naar de IL kijkt is het een class. Het is onveranderlijk in het feit dat geen van de properties gemodificeerd kunnen worden wanneer het gemaakt is. Wanneer je een record type definieert synthetiseert de compiler verschillende andere methoden voor je:
Methoden voor waarde gebaseerde gelijkheidsvergelijkingen
Overschrijven voor GetHashCode()
Kopiëren en klonen van leden
PrintMembers en ToString()
Records ondersteunen inheritance. Je kunt een nieuwe record afgeleid van Person vaststellen:
Je kunt ook records afsluiten om verdere afleidingen te voorkomen:
De compiler synthetiseert verschillende versies van de methoden hierboven. De methode signaturen hangen af van het feit of de record type afgesloten is en of de directe base class onderwerp is. Records moeten de volgende mogelijkheden hebben:
Gelijkheid is waarde gebaseerd en omvat een check dat de types matchen. Een Student kan bijvoorbeeld niet gelijk zijn aan een Person, zelfs als de twee records dezelfde naam delen.
Records hebben een consistente string representatie aangemaakt voor jou.
Records ondersteunen kopieer constructie. Een goede kopieer constructie moet inheritance hiërarchieën bevatten en properties toegevoegd door developers.
Records kunnen gekopieerd worden met modificatie. Deze kopieer en modificeer operaties ondersteunen non-destructieve verandering.
In aanvulling op de bekende Equals overloads, operator == en operator !=, is de compiler synthetiseren een nieuwe EqualityContract property. De property geeft een Type onderwerp terug dat matcht met de type record. Wanneer de base type object is dan is de property virtual. Wanneer de base type een ander record type is, dan is de property een override. Wanneer de record type sealed is, is de property ook sealed. De gesynthetiseerde GetHashCode gebruikt de GetHashCode van alle properties en velden verklaard in de base type en de record type. Deze gesynthetiseerde methoden dwingen waarde gebaseerde gelijkheid door inheritance hiërarchie. Dat betekent dat een Student nooit gelijk gezien kan worden met een Person met dezelfde naam. De types van de twee records moeten matchen evenals alle properties tussen de record types.
Records hebben ook een gesynthetiseerde bouwer en een ’clone’ methode voor het maken van kopieën. De gesynthetiseerde bouwer heeft een enkele parameter van de record type. Het produceert een nieuwe record met dezelfde waarden voor alle properties van de record. Deze bouwer is privé wanneer de record gesloten is, anders is het beschermd. De gesynthetiseerde ‘clone’ methode ondersteunt kopieer constructie voor record hiërarchieën. De term ‘clone’ is tussen haakjes omdat de daadwerkelijke naam is gegenereerd door de compiler. Je kunt geen methode maken genaamd Clone in een record type. De gesynthetiseerde ‘clone’ methode geeft de type record terug wanneer hij gekopieerd is door het gebruik van virtuele verzending. De compiler voegt verschillende modifiers toe voor de ‘clone’ methode afhankelijk van de toegankelijkheid modifiers op de record:
Wanneer de record type abstract is, is de ‘clone’ methode ook abstract. Wanneer de base type niet object is, is de methode ook override.
Voor record types die niet abstract zijn wanneer de base type object is:
- Wanneer de record sealed is, worden geen additionele modifiers toegevoegd aan de ‘clone’ methode (dit betekent dat het niet virtual is).
- Wanneer de record sealed is, is de ‘clone’ methode virtual.
Voor record types die niet abstract zijn wanneer de base type niet object is:
- Wanneer de record sealed is, is de ‘clone’ methode ook sealed.
- Wanneer de record niet sealed is, is de ‘clone’ methode override.
Het resultaat van al deze regels is dat de gelijkheid consistent is geïmplementeerd door elke hiërarchie van record types. Twee records zijn gelijk aan elkaar als hun properties gelijk zijn en hun types gelijk zijn, zoals in het volgende voorbeeld:
De compiler synthetiseert twee methoden die geprinte output ondersteunen: een ToString() override en PrintMembers. De Printmembers pakt een System.Text.StringBuilder als zijn argument. Het voegt een door komma gescheiden lijst van property namen toe en waarden voor alle properties in de record type. PrintMembers roept de base implementatie op voor elke record afgeleid van andere records. De ToString() override geeft de string gemaakt door PrintMembers terug. Bijvooorbeeld, de ToString() methode voor Student geeft een string terug zoals de volgende code:
De voorbeelden die tot nu toe voorbij gekomen zijn maken gebruik van traditionele syntax om properties vast te stellen. Er is een preciezere vorm genaamd positional records. Hier zijn drie record types eerder gedefinieerd als positionele records:
Deze verklaringen creëren dezelfde functionaliteit als de eerdere versie (met wat extra features). Deze verklaringen eindigen met een puntkomma in plaats van haakjes omdat deze records geen additionele methoden toevoegen. Je kunt een body toevoegen en omvatten ook elke additionele methoden:
De compiler maakt een Deconstruct methode aan voor positionele records. De Deconstruct methode heeft parameters die matchen met de namen van alle publieke properties in de record type. De Deconstruct methode kan gebruikt worden om de record te deconstrueren in zijn component properties:
Tenslotte, worden with expressions ondersteund door records. Een with expression instrueert de compiler om een kopie te maken van een record. Maar dit met gespecificeerde properties gemodificeerd:
De vorige regel creëert een nieuwe Person record waar de LastName property een kopie is van person en de FirstName “Paul” is. Je kunt elk aantal properties in een with expression vaststellen. Je kunt ook gebruik maken van with expressions om een exacte kopie te maken. Je specificeert de lege set voor de properties om te modificeren:
Alle gesynthetiseerde leden behalve de ‘clone’ methode kan geschreven zijn door jou. Als een record type een methode heeft die de signatuur van welke gesynthetiseerde methode dan ook matcht, synthetiseert de compiler die methode niet.
Meer lezen hierover? Bekijk deze link: https://devblogs.microsoft.com/dotnet/c-9-0-on-the-record/?ocid=AID3017126