C# 11 Features

  1. Inleiding
  2. Raw String Literals
  3. Required keyword
  4. List Pattern Matching
  5. File access modifier
  6. UTF8 string literal
  7. Slot

Inleiding

Microsoft komt om de zoveel tijd met een nieuwe versie van C# en zo is in november 2022 .Net 7 met C# 11 uitgebracht.

We laten in deze post een aantal features de revue passeren die nieuw zijn in C# 11. Features waar we het over gaan hebben in deze post zijn de Raw String Literals, de required keyword, de List Pattern matching, de File access modifier en de UTF8- string literal.

We bespreken in deze post het één en ander aan de hand van een .Net 7 C# 11 Console App. De voorbeeldcode kun je in deze GitHub repository vinden.

up | down

Raw String Literals

Raw String Literals geven je meer controle over multi line strings en minder gedoe met escape sequences (“\”, “\r”, “\n” etc.).

Laten we nog een keer op een rijtje zetten wat we in C# aan strings hebben voordat we naar de Raw String Literal gaan kijken. We hebben de ingebouwde string gegevenstype:

// Gewone string
// De "\" heeft een speciale betekenis.
// daarom een escape sequence
string volledigPad = "C:\\Mijn documenten\\Tanga's.xslx";
string tekst = "Bestand: ";
Console.WriteLine(tekst + volledigPad);

En we hebben de verbatim string (te herkennen is aan de @) en met de verbatim string wordt de backslash als ontsnappingsteken genegeerd:

//Verbatim String
tekst = @"Bestand: C:\Mijn documenten\Tanga's.xslx";
Console.WriteLine(tekst);

Daarnaast hebben we de interpolated string (te herkennen is aan de @) en daar zijn escape sequences wel weer nodig:

// Interpolated String
tekst = $"Bestand: {volledigPad}";
Console.WriteLine(tekst);

Maar we blijven gedoe houden met escape sequences en meerdere regels want hoe gaat het eruit zien voor bijvoorbeeld XML- of JSON? Gelukkig hebben we sinds C# 11 de Raw String Literal. We definiëren deze variabelen die we in de Raw String Literals willen opnemen:

// Raw String Literal
string eigenaar1 = "Marisca";
string eigenaar2 = "Sandra";

En we kunnen bijvoorbeeld onderstaande XML definiëren waarbij we de tags in laten springen:

En eenzelfde verhaal voor JSON:

De Raw String Literal moet met minimaal drie double quotes beginnen en met drie double quotes eindigen. Let ook op de verticale lijn die in de Visual Studio IDE wordt aangegeven als zijnde de kantlijn.

En we krijgen uiteindelijk dit resultaat;

Ten slotte nog een voorbeeld hoe je gebruik kunt maken van een switch-expressie in een Raw String Literal:

// switch statement in een Raw String Literal 
int Regio = 2;
Console.WriteLine(
$"""
Nummer {Regio} staat voor regio {Regio switch
{
 1 => "Noord",
 2 => "Midden",
 3 => "Zuid",
  _ => "Onbekend"
}}.

""");

(de tekst Nummer 2 staat voor regio Midden zal getoond worden)

up | down

Required keyword

Nieuw in C# 11 is de required keyword. We definiëren ter illustratie onderstaande klasse en de attributen ID en Omschrijving krijgen daarbij de required keyword:

public class EIGENAAR
{
 public required int ID { get; set; }

 public required string Omschrijving { get; set; }

 public string? Voornaam { get; set; }

 public string? Achternaam { get; set; }

 public string? Regio { get; set; }
}

Het gevolg is dat we een melding van de compiler krijgen zodra we een object instantiëren uit de klasse en we bij het instantiëren geen waarden toekennen aan de ID en de Omschrijving:

Het instantiëren naar een object lukt uiteindelijk wel als we bij het instantiëren wel waarden toekennen aan de ID en de Omschrijving:

// required keyword
EIGENAAR eigenaar = 
new EIGENAAR 
{ID = 1, Omschrijving = "de auto van Marisca" };

Console.WriteLine
($"Dit is {eigenaar.Omschrijving} en 
de ID is {eigenaar.ID}.");

up | down

List Pattern Matching

Pattern matching is een techniek waarin een expressie wordt getest op het voorkomen van een bepaald kenmerk. Pattern matching werd voor het eerst in C# 7 geïntroduceerd.

We hebben verschillende pattern matchings en zo is er bijvoorbeeld de constant pattern matching waarin een expressie wordt afgezet tegenover een constante:

Rechthoek rechthoek = 
new Rechthoek { Breedte = 7, Langte = 3 };

public void ObjectIsNull(Rechthoek rechthoek)
{
 // de expressie: een object
 // de constante: een Null-waarde
 // is de expressie (object rechthoek) 
// een Null-waarde?
 if (rechthoek is null)
 {
  throw new 
  ArgumentNullException(nameof(rechthoek));
 }
}

In C# 11 hebben ze de List Pattern matching toegevoegd en deze techniek is interessant als je van bepaalde elementen in een List of Array wilt weten of ze aan een bepaald patroon voldoen:

// List Patterns
int[] nummers = { 1, 2, 3, };

// patroon 1
var patroon1 = nummers is [1,2,3];
if (patroon1) Console.WriteLine
("De elementen in de array voldoen aan patroon1\n
(precies drie dezelfde elementen).\n");

// patroon 2
var patroon2 = nummers is [1, 2];
if (!patroon2) Console.WriteLine(
"De elementen in de array voldoen niet aan patroon2\n
(patroon2 begint en 
bevat weliswaar dezelfde twee elementen,\n
maar het zijn er twee terwijl 
de array drie elementen bevat).\n");

// patroon 3
var patroon3 = nummers is [1, 2, _];
if (patroon3) Console.WriteLine(
"De elementen in de array voldoen aan patroon3\n
(het begint met twee dezelfde elementen en 
wat daarna komt is irrelevant).\n");

// patroon 4
var patroon4 = nummers is [1, 2, > 2];
if (patroon4) Console.WriteLine(
"De elementen in de array voldoen aan patroon4\n
(het begint met twee dezelfde elementen en 
het derde element is groter dan 2).\n");

Resultaat:

up | down

File access modifier

C# kent een aantal access modifiers (public, private, internal, protected) en de access modifiers hebben met C# 11 een nieuwe telg erbij want de file access modifier is met C# 11 toegevoegd aan dat illustere gezelschap.

Als we bijvoorbeeld in een bestand een bepaalde class definiëren met een file access modifier en buiten dat bestand ook een klasse aanwezig is met eenzelfde naam dan zal alles wat zich in dat bestand bevindt alleen maar die file access modifier class gebruiken in het eigen bestand. Ze zullen niet verder kijken naar de class buiten dat bestand met eenzelfde naam. De compiler zal er verder ook niet moeilijk over doen en zal niet zeuren over conflicten.

De file access modifier is met name interessant voor code generators. De code generator wil binnen zijn eigen bestand zijn dingen doen zonder last te krijgen van een compiler die gaat roepen dat buiten het bestand in de assembly dingen aanwezig zijn met eenzelfde naam en dat dat conflicten gaat geven.

We illustrereren het één en ander aan de hand van het volgende voorbeeld:

We hebben een “Berekening_Correct“-klasse en een “Berekening_Exoot“-klasse. Allebei de klassen maken gebruik van een “RekenRegels“-klasse met methoden die qua naamgeving aan elkaar gelijk zijn. En dit kan voor de compiler problemen geven want er zijn twee klassen met eenzelfde naam met methoden die ook eenzelfde naam hebben.

De file access modifier biedt uitkomst voor zulke situaties want “Berekening_Exoot” zal dankzij de file access modifier alleen maar kijken naar klasse “RekenRegels” in Bestand2.cs. Dat er een klasse “RekenRegels” in Bestand1.cs aanwezig is, dat is voor “Berekening_Exoot” niet relevant en de compiler zal ook niet lastig doen door de file access modifier.

We instantiëren objecten uit de “Berekening_Correct“-klasse en de “Berekening_Exoot“-klasse en we roepen vanuit die klassen de methoden aan van klasse “Rekenregels“:

// C# file access modifier //
int getal1 = 1;
int getal2 = 2;

Berekening_Correct berekening_correct = 
new Berekening_Correct();

Berekening_Exoot berekening_exoot = 
new Berekening_Exoot();

Console.WriteLine(
$"De correcte manier van optellen: 
{getal1} + {getal2}  is 
{berekening_correct.Optellen(getal1, getal2)}");

Console.WriteLine(
$"De exotische manier van optellen:
{getal1} en {getal2} is
{berekening_exoot.Optellen(getal1, getal2)}");

Console.WriteLine(
$"De correcte manier van verminderen:
{getal1} - {getal2}  is 
{berekening_correct.Verminderen(getal1, getal2)}");

Console.WriteLine(
$"De exotische manier van verminderen:
{getal1} - {getal2}  is
{berekening_exoot.Verminderen(getal1, getal2)}\n");

En we krijgen dit resultaat waarbij de exotische methoden een bijzonder resultaat teruggeven. Als je twee getallen optelt dan wordt gratis en voor niks 1000 extra erbij opgeteld en als je twee getallen van elkaar aftrekt dan is het resultaat nul als het resultaat kleiner is dan nul.

De methoden “.Optellen()” en”.Verminderen()” in Bestand2.cs wijken af van de publieke methodes in Bestand1.cs en we kunnen die methoden met de file access modifier definiëren in Bestand2.cs zonder dat dat conflicten oplevert met de publieke methoden in Bestand1.cs.

up | down

UTF8 string literal

Het begon ooit met ASCII (American Standard Code for Information Interchange) waarbij 7 bits (128 mogelijkheden) al volstonden als character-encoding technique voor het Engelse alfabet en er ook nog ruimte was voor enkele speciale besturingstekens.

De zeven bits waren al snel niet genoeg om de vele andere talen buiten het Engels te kunnen ondersteunen en ANSI (American National Standards Institution) en UTF8 (Unicode Transformation Format 8-bit coding system) deden hun intrede waarbij deze character-encoding technieken wel in staat zijn tot het doen ondersteunen van meerdere talen inclusief extended characters en emoijs.

In C# 11 is er de UTF-8 string literal zijnde een u8-suffix. De suffix kan gebruikt worden voor UTF-8 byte arrays waarmee de code wat eenvoudiger kan worden:

// UTF8 string literals
var data = 
new ReadOnlySpan<byte>(new byte[] { 65, 66, 67 });

data = "ABC"u8;

data = new byte[] { 65, 66, 67 };

data = "ABC"u8.ToArray();

up | down

Slot

Een aantal CSharp 11 features hebben we in deze post besproken. Voor elke nieuwe release van C# kan weer de vraag gesteld worden of de meeste C# developers iets met die nieuwe features gaan doen.

Gaan ze inderdaad de features gebruiken om zo te komen tot beter te doorgronden code? Of is een kleine verandering in de manier van programmeren al te veel gevraagd waarbij vasthouden aan een cryptische, voor anderen niet te doorgronden programmeerstijl toch gemakkelijker is? Ik sta zelf niet onwelwillend tegenover de nieuwe features en ik zal ze zeker gebruiken als ze de leesbaarheid van mijn code verbeteren.

Hopelijk ben je met deze posting weer wat wijzer geworden en ik hoop je weer terug te zien in één van mijn volgende blog posts. Wil je weten wat ik nog meer over C# heb geschreven? Hit the C# button…

up

Laat een reactie achter

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *