14. Feb 2022
Lesedauer 8 Min.
C# 10: Neue Features
CSharp mit .NET 6 und VS 2022
Die interessantesten Neuerungen von C# 10 und wie man sie einsetzt.

Laut Tiobe-Index [1] belegt C# den fünften Platz im Ranking der Programmiersprachen (Stand Dezember 2021) und zählt somit zu den besonders gefragten Programmiersprachen. Ein guter Grund, sich mit den neuen Sprachfeatures auseinanderzusetzen, die C# in Version 10 mitbringt. Um die neuen Features zu untersuchen, wurde die Preview-Version 4.1 von Visual Studio 2022 verwendet.
Globale Using-Anweisungen
Using-Anweisungen wie zum Beispiel using System, using System.Collection.Generic oder System.Linq sind sehr populär und werden häufig in zahlreiche Dateien eines Projekts eingebunden [2]. Um diese Anweisungen nicht in jede Projektdatei immer wieder aufs Neue einfügen zu müssen, hat Microsoft global gültige Using-Statements eingeführt. Diese werden einmal als global deklariert und stehen anschließend in allen Dateien zu Verfügung.Bild 1 zeigt ein kleines Beispielprogramm, welches neben der Datei Program.cs auch noch eine Datei mit Erweiterungsmethoden für den Typ IEnumerable<int> definiert. Diese erfordern die farbig markierten Using-Anweisungen.
Wiederkehrende Using-Anweisungen(Bild 1)
Autor
Die Anweisung using System.Collections.Generic; wird in beiden Dateien benötigt und ist ein Kandidat für einen Namespace, der überall verfügbar sein sollte. Bild 2 zeigt die Datei Globals.cs, welche die Using-Befehle enthält, die überall im Projekt verfügbar sind. Durch die Definition globaler Using-Befehle wird im Beispiel aus Bild 2 das Einbinden von System für die Ausgabe mittels Console.WriteLine(content); überflüssig. Dasselbe gilt für die Anweisung using System.Collections.Generic;. Ist sie dennoch in einer Code-Datei des Projekts enthalten, macht Visual Studio darauf aufmerksam, wie es in Bild 3 zu sehen ist.

Globale Using-Befehle(Bild 2)
Autor

Visual Studio 2022 markiertüberflüssige Using-Befehle(Bild 3)
Autor
Die globalen Using-Anweisungen können zudem noch eine Stufe tiefer verwendet werden, um im Code Schreibarbeit einzusparen, beispielsweise so:
global <span class="hljs-keyword">using</span> <span class="hljs-keyword">static</span> System.<span class="hljs-built_in">Console</span>;
Der Effekt: Anstelle von Console.WriteLine(numbers.Event()); kann im Code schlicht WriteLine(numbers.Event()); geschrieben werden.Fehlt ein Using-Befehl, von dem man dachte, dass er bereits vorhanden ist, liefert Visual Studio einen Fehlerhinweis – siehe Bild 4. Das verführt dazu, die globalen Using-Befehle über mehrere Code-Dateien zu streuen. Besser ist es, von vornherein eine Datei Globals.cs anzulegen und alle globalen Usings darin zu platzieren.

Der Tooltipin Visual Studio zeigt Zusatzinformationen(Bild 4)
Autor
Implizite Using-Anweisungen
Bei neu angelegten Projekten sind bereits einige Namespaces implizit als global markiert. Bild 5 zeigt ein Beispiel, in dem Typen aus den Namensräumen System, System.Net.Http und System.Collections.Generic verwendet werden, ohne dass explizit globale Usings definiert werden mussten. Um dies zu ermöglichen, muss in der Datei csproj die folgende Einstellung zu finden sein:
Implizite Usings(Bild 5)
Autor
<span class="hljs-tag"><<span class="hljs-name">ImplicitUsings</span>></span>enable<span class="hljs-tag"></<span class="hljs-name">ImplicitUsings</span>></span>
Durch das Entladen des Projekts kann diese Datei betrachtet und gegebenenfalls editiert werden, vergleiche Bild 6.

Implizite Usingsentladen(Bild 6)
Autor
Dieses Feature ist nur bei neuen Projekten aktiviert. Dass es bei bereits bestehenden Projekten deaktiviert ist, hängt damit zusammen, dass das automatische Aktivieren zu einer veränderten Art und Weise führen kann, wie der Compiler den Code interpretiert, wodurch Probleme bei der Aktualisierung von .NET 5 auf .NET 6 auftreten könnten.An dieser Stelle könnte man sich die Fragen stellen, wie dies eigentlich funktioniert und welche Namespaces automatisch für die gesamte Anwendung eingebunden werden.Die Antwort auf diese Frage ist im obj-Verzeichnis in der konfigurierten Build-Konfiguration (zum Beispiel Debug) im Unterordner net6.0 zu finden. In diesem Verzeichnis befindet sich eine Datei namens [Projektname].GlobalUsings.g.cs, welche die entsprechenden Anweisungen enthält, vergleiche Bild 7.

Automatischeimplizite Usings(Bild 7)
Autor
FileScoped Namespaces
Die Empfehlung bei Namespaces ist, dass je Datei nur ein Namespace vorhanden sein sollte. Bisher war es notwendig, nach dem Namen des Namespace eine öffnende und eine schließende Klammer zu verwenden. Hier ein Beispiel:
// <span class="hljs-type">Namespace</span> mit <span class="hljs-type">Klammern</span>
namespace dotnetpro
{
public class <span class="hljs-type">Program</span>
{
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">void</span> <span class="hljs-type">Main</span>()
{
<span class="hljs-type">Console</span>.<span class="hljs-type">WriteLine</span>(new <span class="hljs-type">Customer</span>(<span class="hljs-string">"dotnetpro"</span>,
<span class="hljs-string">"Christian"</span>, <span class="hljs-string">"Havel"</span>));
}
}
public record <span class="hljs-type">Customer</span>(
<span class="hljs-built_in">string</span> companyName,
<span class="hljs-built_in">string</span> firstName,
<span class="hljs-built_in">string</span> lastName);
}
Das dem Namespace zugehörige Klammernpaar { ... } führt zur ersten Einrückung des Codes. Wie zuvor erwähnt gilt die Empfehlung, dass eine Datei nur einen Namespace enthalten sollte. Microsoft hat deshalb die FileScoped Namespaces eingeführt. Dabei wird der Namespace mit einem Semikolon abgeschlossen, die beiden Klammern sind nicht mehr notwendig, wodurch auch die erste Einrückung entfällt:
// <span class="hljs-type">Namespace</span> ohne <span class="hljs-type">Klammern</span>
namespace dotnetpro;
public class <span class="hljs-type">Program</span>
{
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">void</span> <span class="hljs-type">Main</span>()
{
<span class="hljs-type">Console</span>.<span class="hljs-type">WriteLine</span>(new <span class="hljs-type">Customer</span>(
<span class="hljs-string">"dotnetpro"</span>, <span class="hljs-string">"Christian"</span>, <span class="hljs-string">"Havel"</span>));
}
}
public record <span class="hljs-type">Customer</span>(<span class="hljs-built_in">string</span> companyName,
<span class="hljs-built_in">string</span> firstName, <span class="hljs-built_in">string</span> lastName);
Extended Property Patterns
Mit den vorangegangenen C#-Sprachversionen wurden die sogenannten Property Patterns eingeführt, mit denen sich der Befehl case in einer Switch-Anweisung auf eingebettete Members beziehen konnte. Listing 1 zeigt ein Beispiel, in dem zwei record-Typen definiert sind. Das SpaceShip entstammt einem Planetensystem, in dem eine bestimmte Sprache gesprochen wird.Listing 1: Property Patterns (Klammern)
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"></span>) </span><br/>{ <br/> <span class="hljs-keyword">var</span> spaceShips = <span class="hljs-keyword">new</span>[] <br/> { <br/> <span class="hljs-keyword">new</span> SpaceShip(<span class="hljs-string">"X-Wing"</span>, <span class="hljs-keyword">new</span> PlanetSystem( <br/> <span class="hljs-string">"Epsilon Eridani"</span>)), <br/> <span class="hljs-keyword">new</span> SpaceShip(<span class="hljs-string">"B-Wing"</span>, <span class="hljs-keyword">new</span> PlanetSystem( <br/> <span class="hljs-string">"Alpha Centauri"</span>)), <br/> <span class="hljs-keyword">new</span> SpaceShip(<span class="hljs-string">"Falcon"</span>, <span class="hljs-literal">null</span>) <br/> }; <br/><br/> <span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> spaceShip <span class="hljs-keyword">in</span> spaceShips) <br/> Console.WriteLine(GetLanguage(spaceShip)); <br/> } <br/><br/> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> <span class="hljs-title">GetLanguage</span>(<span class="hljs-params"> </span></span><br/><span class="hljs-function"><span class="hljs-params"> SpaceShip spaceShip</span>) </span><br/><span class="hljs-function"> </span>{ <br/> <span class="hljs-keyword">return</span> spaceShip <span class="hljs-keyword">switch</span> <br/> { <br/> SpaceShip { PlanetSystem: { <br/> star: <span class="hljs-string">"Epsilon Eridani"</span> } } =&gt; <span class="hljs-string">"German"</span>, <br/> SpaceShip { PlanetSystem: { <br/> star: <span class="hljs-string">"Alpha Centauri"</span> } } =&gt; <span class="hljs-string">"Klingon"</span>, <br/> _ =&gt; <span class="hljs-string">"unknown"</span> <br/> }; <br/> } <br/><br/> <span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">SpaceShip</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> name, </span></span><br/><span class="hljs-function"><span class="hljs-params"> PlanetSystem PlanetSystem</span>)</span>; <br/> <span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">PlanetSystem</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> star</span>)</span>;
In der Switch-Anweisung wird der eingebettete Member SpaceShip -> PlanetSystem -> Star für die Ermittlung der Sprache ausgewertet. Betrachtet man andere Programmiersprachen, so ist die Notation mit den geschweiften Klammern eher ungewöhnlich. Üblicher ist der Zugriff auf die Member mit Punkten. Microsoft hat dies in Version 10 nun optimiert.Listing 2 zeigt die Funktion mit der nun verfügbaren Punktnotation, die den Zugriff auf die eingebetteten Member einfacher darstellt.
Listing 2: Property Patterns (Punktnotation)
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> string GetLanguage(<br/> SpaceShip spaceShip) <br/>{ <br/> <span class="hljs-keyword">return</span> spaceShip <span class="hljs-keyword">switch</span> <br/> { <br/> {PlanetSystem.star: <span class="hljs-string">"Epsilon Eridani"</span> } =&gt; <br/> <span class="hljs-string">"German"</span>, <br/> {PlanetSystem.star: <span class="hljs-string">"Alpha Centauri"</span> } =&gt; <br/> <span class="hljs-string">"Klingon"</span>, <br/> _ =&gt; <span class="hljs-string">"unknown"</span> <br/> }; <br/> }
Zu erwähnen ist, dass die definierten Switch-Cases nicht zutreffen und der default-Wert verwendet wird (hier unknown), wenn der Member, der im Switch-Ausdruck geprüft wird (PlanetSystem), nicht definiert, das heißt null ist (new SpaceShip(“Falcon“, null)), siehe Bild 8.

Ist das Planetensystem „null“gibt es keine Fehlermeldung, sondern es wird der default-Wert zurückgeliefert(Bild 8)
Autor
Constant Interpolated Strings
Hin und wieder kann es vorkommen, dass interpolierte Strings ausschließlich aus konstanten Strings generiert werden. Wie Bild 9 zeigt, war es vor C# 10 nicht möglich, einen solchen interpolierten String einer Konstanten zuzuweisen. Wird die Sprachversion in Visual Studio auf C# 10 geändert, akzeptiert der Compiler das neue Feature, wie Sie Bild 10 entnehmen können.
Konstante String-Interpolationmit C# 9(Bild 9)
Autor

C# 10 erlaubtConstant Interpolated Strings(Bild 10)
Autor
Struktur DateOnly
Wie der Name schon suggeriert, repräsentiert diese Datenstruktur ein Datum ohne eine zeitliche Angabe [3, 4]. Die Erzeugung kann auf verschiedene Arten erfolgen, beispielsweise über den Aufruf des Konstruktors:
<span class="hljs-keyword">var</span> dateOnly = <span class="hljs-keyword">new</span> <span class="hljs-type">DateOnly</span>(<span class="hljs-number">1976</span>, <span class="hljs-number">8</span>, <span class="hljs-number">8</span>);
Oder über eine der verfügbaren Factory-Funktionen, die das Datum aus einem bestehenden DateTime-Objekt übernimmt:
<span class="hljs-keyword">var</span> date =
DateOnly.FromDateTime(
<span class="hljs-built_in">DateTime</span>.Now);
Wie bei DateTime kann aber auch ein String geparst werden. Im nachfolgenden Code-Schnipsel sehen Sie die Variante mit TryParse. Auch die Alternative mit der Funktion Parse steht zur Verfügung, wirft aber gegebenenfalls eine Ausnahme.
<span class="hljs-keyword">if</span> (DateOnly.TryParse(
<span class="hljs-string">"28/09/2021"</span>,
<span class="hljs-literal">new</span> CultureInfo(<span class="hljs-string">"en-GB"</span>),
DateTimeStyles.<span class="hljs-literal">None</span>,
out <span class="hljs-built_in">var</span> <span class="hljs-built_in">date</span>))
{
Console.WriteLine(<span class="hljs-built_in">date</span>);
}
Da die Menschen verschiedener Länder Datumsangaben unterschiedlich interpretieren, wird das Parsen von Datumsangaben bei C# durch die Culture beeinflusst.Die im obigen Code-Schnipsel genutzte Anweisung CultureInfo(“en-GB“) soll dafür sorgen, dass das Datum aus einer Zeichenfolge im Format Tag/Monat/Jahr geparst wird.Durch die Funktionsaufrufe AddDays, AddMonths und AddYears wird eine neue Instanz mit entsprechend angepasstem Datum erstellt:
// Ein neues DateOnly erstellen
<span class="hljs-built_in">var</span> dateOnly = <span class="hljs-built_in">new</span> DateOnly(<span class="hljs-number">1976</span>, <span class="hljs-number">8</span>, <span class="hljs-number">8</span>);
dateOnly.AddDays(<span class="hljs-number">1</span>).AddMonths(<span class="hljs-number">1</span>).AddYears(<span class="hljs-number">1</span>);
Struktur TimeOnly
Ebenfalls neu ist die Struktur TimeOnly. Sie repräsentiert eine Uhrzeit ohne das zugehörige Datum [3, 4]. Sie ist genauso einfach zu handhaben wie DateOnly, bietet allerdings zwei Besonderheiten. Den Einstieg in diese Struktur zeigt folgende Anweisung, die zur Ausgabe von 00:00 führt:<span class="hljs-selector-tag">Console</span><span class="hljs-selector-class">.WriteLine</span>(<span class="hljs-selector-tag">TimeOnly</span><span class="hljs-selector-class">.MinValue</span>);
Wird dieselbe Anweisung anstelle von MinValue mit dem Zusatz MaxValue genutzt, liefert sie als Ergebnis den String-Wert 23:59 zurück:
<span class="hljs-selector-tag">Console</span><span class="hljs-selector-class">.WriteLine</span>(<span class="hljs-selector-tag">TimeOnly</span><span class="hljs-selector-class">.MaxValue</span>);
TimeOnly bietet zudem die recht praktische Funktion Between, mit der geprüft werden kann, ob eine Uhrzeit zwischen zwei anderen Uhrzeitangaben liegt. Hier ein Beispiel, das prüft, ob es gerade zwischen 10 und 15 Uhr ist:
<span class="hljs-keyword">var</span> startTime = <span class="hljs-keyword">new</span> TimeOnly(<span class="hljs-number">10</span>, <span class="hljs-number">00</span>);
<span class="hljs-keyword">var</span> endTime = <span class="hljs-keyword">new</span> TimeOnly(<span class="hljs-number">15</span>, <span class="hljs-number">00</span>);
<span class="hljs-keyword">var</span> currentTime =
TimeOnly.FromDateTime(DateTime.Now);
<span class="hljs-keyword">bool</span> between =
currentTime.IsBetween(
startTime, endTime);
Console.WriteLine(
<span class="hljs-string">$"start: <span class="hljs-subst">{startTime}</span> end: <span class="hljs-subst">{endTime}</span> </span>
<span class="hljs-string"> current: <span class="hljs-subst">{currentTime}</span> </span>
<span class="hljs-string"> between: <span class="hljs-subst">{between}</span>"</span>);
Die Operatoren der Struktur unterstützen den Vergleich der Instanzen, so führt der nachstehende Code zur Ausgabe start: 10:00 end: 15:00 - start before end: True.
Console.WriteLine(
$"<span class="hljs-keyword">start</span>: {startTime} <span class="hljs-keyword">end</span>: {endTime} -
<span class="hljs-keyword">start</span> <span class="hljs-keyword">before</span> <span class="hljs-keyword">end</span>: {startTime < endTime}<span class="hljs-string">"); </span>
Strings auf null prüfen
Guard-Anweisungen für Eingabeparameter wurden bisher programmiert, wie sie der nachstehende Code zeigt. Und vermutlich fällt darin auch gleich der bewusst eingebaute Fehler beim Auslösen der Ausnahme von firstName auf, welcher als Parameter nameof(name) entgegennimmt anstelle von nameof(firstName).
<span class="hljs-regexp">//</span> Bisherige Prüfung auf <span class="hljs-literal">null</span>
public void Execute(
string name, string firstName, string company)
{
<span class="hljs-keyword">if</span> (name <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>)
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(nameof(name));
<span class="hljs-keyword">if</span> (firstName <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>)
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(nameof(name));
<span class="hljs-keyword">if</span> (company <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>)
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ArgumentNullException(
nameof(company));
<span class="hljs-regexp">//</span> logic
}
Einfacher geht das jetzt mit C# 10 [5]. Die Guard-Anweisungen können wie folgt implementiert werden – ein Fehler wie die oben eingebaute Verwechslung würde sofort auffallen:
// Sicherere Prüfung durch die CallerArgumentExpression
public void <span class="hljs-built_in">Execute</span>(
<span class="hljs-built_in">string</span> name, <span class="hljs-built_in">string</span> firstName, <span class="hljs-built_in">string</span> company)
{
ArgumentNullException.
ThrowIfNull(name)<span class="hljs-comment">; </span>
<span class="hljs-comment"> ArgumentNullException.</span>
<span class="hljs-comment"> ThrowIfNull(firstName); </span>
<span class="hljs-comment"> ArgumentNullException.</span>
<span class="hljs-comment"> ThrowIfNull(company); </span>
<span class="hljs-comment">}</span>
Technisch umgesetzt wurde dies durch das CallerArgumentExpressionAttribute, das zu der Gruppe von Attributen gehört, mit denen Informationen über den Aufrufer ermittelt werden können. Mehr dazu erfahren Sie unter [6] und [7].
Daten in Chunks verarbeiten
Nicht immer ist es sinnvoll, alle Einträge einer Liste in einem Durchgang zu durchlaufen. Deshalb bietet C# 10 eine Erweiterung, um Listen in kleinen Häppchen (Chunks) zu verarbeiten.Der nachfolgende Code zeigt ein Beispiel, mit dem die Chunks aus einem IEnumerable ausgelesen werden:
// Mit Chunks iterieren
<span class="hljs-keyword">var</span> <span class="hljs-keyword">list</span> = Enumerable.Range(<span class="hljs-number">1</span>, <span class="hljs-number">100</span>);
const int size = <span class="hljs-number">10</span>;
int chunkCounter = <span class="hljs-number">0</span>;
<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> chunk <span class="hljs-keyword">in</span> <span class="hljs-keyword">list</span>.Chunk(size)) {
Console.WriteLine($<span class="hljs-string">"##### ChunkNr.: </span>
<span class="hljs-string"> {chunkCounter++}#####"</span>);
<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> item <span class="hljs-keyword">in</span> chunk) {
Console.WriteLine($<span class="hljs-string">"-> {item}"</span>);
}
}
In Bild 11 finden Sie die zum Code gehörende Ausgabe der Chunks auf der Konsole.

Ausgabe von Chunks(Bild 11)
Autor
Erweiterungen von LINQ
Bei Abfragen mit der Erweiterungsmethode FirstOrDefault ist eventuell nicht ganz klar, was diese Funktion liefert, wenn das Wiederfindungsmerkmal nicht gefunden wird.// Default-Wert
<span class="hljs-keyword">var</span> <span class="hljs-keyword">list</span> = new List<int> {<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>};
<span class="hljs-keyword">var</span> required = <span class="hljs-number">4</span>;
<span class="hljs-keyword">var</span> found = <span class="hljs-keyword">list</span>.FirstOrDefault(x => x == required);
Console.WriteLine(found);
In obigem Beispiel wird der Wert 0 ausgegeben, was dem default(int) entspricht. Wäre der Wert von required ebenfalls 0, wäre nicht klar, ob eine Übereinstimmung gefunden wurde, oder ob anstelle dessen der default(int) als Rückgabe geliefert wurde. Mit .NET 6 ist es nun möglich, einen eigenen Standardwert zu definieren:
<span class="hljs-keyword">var</span> found = list.FirstOrDefault(<span class="hljs-function"><span class="hljs-params">x</span> =></span> x == required, <span class="hljs-number">-1</span>);
Bezugnehmend auf das zuvor genannte Problem ist es mit dieser Variante nun einfacher möglich, zu unterscheiden, ob es sich beim Resultat um den Standardwert handelt oder ob der Wert tatsächlich vorhanden war.
PriorityQueue
Bei einer PriorityQueue ist jedes Element mit der Information über seine Priorität versehen. Soll ein Element aus der Queue geholt werden, erfolgt eine Prüfung, welches der Elemente die höchste Priorität hat, und dieses wird geliefert, unabhängig davon, wann es zur Queue hinzugefügt wurde.Das Verhalten ist somit anders als bei einer klassischen Queue, die mit „first in, first out“ (FIFO) arbeitet, und auch anders als bei einem Stack, der mit „last in, first out“ (LIFO) arbeitet. Im folgenden Beispiel werden vier Elemente zu einer Queue hinzugefügt. Der zweite Parameter (Datentyp int) steht für die Priorität, wobei der geringste Wert der höchsten Priorität entspricht.
// <span class="hljs-type">Priority</span> <span class="hljs-type">Queue</span>
<span class="hljs-keyword">var</span> queue = new <span class="hljs-type">PriorityQueue</span><<span class="hljs-built_in">string</span>, <span class="hljs-built_in">int</span>>();
queue.<span class="hljs-type">Enqueue</span>(<span class="hljs-string">"Element A"</span>, <span class="hljs-number">0</span>);
queue.<span class="hljs-type">Enqueue</span>(<span class="hljs-string">"Element B"</span>, <span class="hljs-number">20</span>);
queue.<span class="hljs-type">Enqueue</span>(<span class="hljs-string">"Element C"</span>, <span class="hljs-number">5</span>);
queue.<span class="hljs-type">Enqueue</span>(<span class="hljs-string">"Element D"</span>, <span class="hljs-number">3</span>);
<span class="hljs-keyword">while</span> (queue.<span class="hljs-type">TryDequeue</span>(
<span class="hljs-keyword">out</span> <span class="hljs-built_in">string</span> element, <span class="hljs-keyword">out</span> <span class="hljs-built_in">int</span> priority))
<span class="hljs-type">Console</span>.<span class="hljs-type">WriteLine</span>($<span class="hljs-string">"element: {element} - </span>
<span class="hljs-string"> priority: {priority}"</span>);
In Bild 12 sehen Sie die erwartungsgemäße Ausgabe dieses Code-Schnipsels: ADCB.

Ausgabe einer Priority Queue(Bild 12)
Autor
Erweiterungsfunktionen Max und MaxBy
Mithilfe der Erweiterungsfunktion Max wird der Maximalwert in einer Sequenz ermittelt. Der folgende Code führt somit zur Ausgabe der Zahl 3.
// Die Funktion <span class="hljs-keyword">Max</span>
var list = new <span class="hljs-keyword">List</span><int> { 1, 2, 3 };
Console.WriteLine(list.<span class="hljs-keyword">Max</span>());
Wird Max auf einen komplexen Typ angewendet, so wird der Maximalwert ebenfalls als Integer-Wert zurückgegeben. Anders ist dies bei der Funktion MaxBy. Sie liefert das komplette Objekt zurück, das die Bedingung erfüllt, siehe Bild 13.

Ausgabe von Max und MaxBy(Bild 13)
Autor
// Max <span class="hljs-literal">und</span> MaxBy mit komplexen Typen
<span class="hljs-built_in">var</span> users = <span class="hljs-built_in">new</span> List<User> {
<span class="hljs-built_in">new</span> User(<span class="hljs-string">"Alf"</span>, <span class="hljs-number">35</span>), <span class="hljs-built_in">new</span> User(<span class="hljs-string">"Hubert"</span>, <span class="hljs-number">18</span>),
<span class="hljs-built_in">new</span> User(<span class="hljs-string">"Alfred"</span>, <span class="hljs-number">55</span>)
};
Console.WriteLine(users.Max(x => x.age));
Console.WriteLine(users.MaxBy(x => x.age));
record User(<span class="hljs-built_in">string</span> name, int age);
Fazit
.NET 6 in Kombination mit C# 10 bringt einige praktische Neuerungen mit, die das Programmieren nochmals spürbar bequemer machen. Die neuen Features in Kombination mit Visual Studio 2022 hinterlassen einen erstklassigen Eindruck.Fussnoten
- Tiobe Index, http://www.tiobe.com/tiobe-index
- Global-Using-Direktiven, http://www.dotnetpro.de/SL2203Csharp101
- DateOnly und TimeOnly, http://www.dotnetpro.de/SL2203Csharp102
- DateOnly und TimeOnly, http://www.dotnetpro.de/SL2203Csharp103
- Null argument checks, http://www.dotnetpro.de/SL2203Csharp104
- Jetbrains, Caller Argument Expressions, http://www.dotnetpro.de/SL2203Csharp105
- Microsoft, Caller Argument Expressions, http://www.dotnetpro.de/SL2203Csharp106