
11 jun Enkele nieuwe features voor C# 10
Tegenwoordig wordt elke C# developer elk jaar getrakteerd op een nieuwe versie van de C# programmeertaal. Enkele jaren geleden heeft Microsoft zijn C# compiler helemaal terug opnieuw ontwikkeld. De vorige versie van de C# compiler werd in C++ ontwikkeld en de nieuwe, huidige versie van de C# werd in C# ontwikkeld, is volledig open source en terug te vinden op GitHub. Men zegt vaak: “Een programmeertaal is pas echt volwassen als het mogelijk is om zijn compiler opnieuw te schrijven in de programmeertaal zelf”. Dit wil zeggen dat C# code vandaag, dankzij deze nieuwe compiler, dus wordt gecompileerd door een programma dat ook in C# werd geschreven!
Dankzij deze evolutitie is de introductie van nieuwe features in de C# programmeertaal veel eenvoudiger geworden voor de ontwikkelaars binnen Microsoft. De nieuwe C# compiler is minder ouderwets en veel flexibeler opgebouwd waardoor sneller ontwikkeld, beter geïntegreerd en gemakkelijker getest kan worden. Op deze manier kunnen we, eind 2021, uitkijken naar de release van de 10de iteratie van de C# programmeertaal.
Op de Microsoft conferentie Build, die laatst werd georganiseerd in mei 2021, werden zo enkele van deze nieuwe features binnen C# 10 aangekondigd en besproken.
“Record structs”
Vorig jaar, in C# 9, werden records geïntroduceerd als nieuwe constructies om objecten te bouwen. Een record is eigenlijk een immutable klasse: een klasse die je na het instantiëren niet meer kan wijzigen. Voor records worden door de compiler automatisch een heleboel hulpmiddelen gecreëerd waardoor je records gemakkelijk kan vergelijken “by value” en deze ook eenvoudig kan kopiëren.
Onderliggend zal een record ook een klasse zijn en daarom worden records op het “Heap”-geheugen beheerd. Als je toch liever gebruik maakt van structs, omdat deze op het “Stack”-geheugen worden beheerd, had je voordien dus niet de mogelijkheid om de voordelen van records in combinatie met struct te gebruiken. Vanaf C# 10 zal je dus een record struct kunnen bouwen door deze twee keywords te combineren.
record struct Persoon { public string Naam { get; init; } public byte Leeftijd { get; init; } }
“Required properties”
Vandaag zijn er in C# twee mogelijkheden om properties van objecten te zetten tijdens het instantiëren van een object. Enerzijds kan je gebruik maken van een constructor. Anderzijds kan je gebruik maken van “Object Initializers”. Constructors zijn handig om developers te verplichten bepaalde properties mee te geven bij de creatie van een object. Dit is vooral handig als een object niet nuttig zou zijn zonder een bepaalde property. Vandaag wordt er door C# developers vaak gekozen voor “Object Initializers” omdat er hierdoor minder extra werk nodig om een constructor te definiëren.
Constructor
Zo ziet het gebruik van een mogelijke constructor voor een persoon er vandaag uit.
var persoon = new Persoon( "Jan", 32 );
Object Initializer
Zo ziet een “Object Initializer” voor een persoon er vandaag uit.
var persoon = new Persoon { Naam = "Jan", Leeftijd = 32 };
Required properties
Elke property zal de mogelijkheid hebben om verplicht te zijn door gebruik te maken van het nieuwe “required” keyword.
class Persoon { public required string Naam { get; set; } public required byte Leeftijd { get; set; } }
Als een developer een instantie maakt van bovenstaande “Persoon”, dan zal hij altijd beide properties moeten zetten. Doet hij dit niet, dan zal de C# compiler hiervoor een compilatiefout genereren.
“The field keyword”
Automatische properties, met getter en setter, zijn al een tijdje beschikbaar in C#. In de eerste jaren na het onstaan van C# was je altijd verplicht om een property te voorzien met een backing field. Dit zag er dan bijvoorbeeld zo uit:
class Persoon { private string _naam; public string Naam { get { return _naam; } set { _naam = value; } } public DateTime Geboortedatum { get; set; } }
Tegenwoordig zal de C# compiler dit automatisch voor ons genereren als we automatisch properties gebruiken:
class Persoon { public string Naam { get; set; } }
In de praktijk wil je soms extra logica toevoegen bij het opvragen of wijzigen van een property waardoor je dus verplicht was dit backing field toch manueel aan te maken. In de toekomst zal dit, dankzij het nieuwe “field” keyword, weer gemakkelijk worden.
class Persoon { public string Naam { get; set; } public DateTime Geboortedatum { get; set => field = value.Date(); } }
Dit lukt natuurlijk enkel als het datatype voor de property en het backing field hetzelfde is. Het keyword “field” verwijst dan naar een private variabele met hetzelfde datatype.
“File-scoped namespaces”
Al sinds de eerste versie van C# worden namespaces gedeclareerd door het keyword namespace, gevolgd door de naam en een scope via de bekende C# accolades. Aangezien de meeste developers maar één namespace declareren per *.cs codebestand kan hier wat identatie bespaard worden dankzij file-scoped namespaces. Een namsepace zonder scope is daardoor automatisch gescoped voor het gehele codebestand:
namespace MijnNamespace; public class Persoon { public string Naam { get; set; } }
Op deze manier besparen we eindelijke weer wat horizontale ruimte.
“Global usings”
Een fenomeen dat voor elke C# programmeer heel bekend in de ogen zal klinken is de overvloed aan using statements aan het begin van elke C# codebestand. Vaak heb je vaak dezelfde namespaces nodig doorheen je hele project en moet je deze dus ook in elke codebestand apart gaan introduceren. Dankzij de nieuwe global using keywords zal je dus een afzonderlijk codebestand kunnen maken waarin enkel global using statements worden opgesomd dewelke dan automatisch beschikbaar zullen zijn voor alle codebestanden in het gehele project.
global using System; global using System.Console; global using System.IO; global using System.Linq; global using System.Text; global using static System.Console;
In tegenstelling tot de vorige feature, zorgt dit dan weer voor extra verticale ruimte.