Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 8 Min.

C# 10: Neue Features

Die interessantesten Neuerungen von C# 10 und wie man sie einsetzt.
© dotnetpro
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 ­Name­space, 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 Schreib­arbeit 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">&lt;<span class="hljs-name">ImplicitUsings</span>&gt;</span>enable<span class="hljs-tag">&lt;/<span class="hljs-name">ImplicitUsings</span>&gt;</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 Name­space 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)
&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; &lt;span class="hljs-title"&gt;Main&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) &lt;/span&gt;&lt;br/&gt;{ &lt;br/&gt;  &lt;span class="hljs-keyword"&gt;var&lt;/span&gt; spaceShips = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt;[] &lt;br/&gt;  { &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; SpaceShip(&lt;span class="hljs-string"&gt;"X-Wing"&lt;/span&gt;, &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; PlanetSystem( &lt;br/&gt;      &lt;span class="hljs-string"&gt;"Epsilon Eridani"&lt;/span&gt;)), &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; SpaceShip(&lt;span class="hljs-string"&gt;"B-Wing"&lt;/span&gt;, &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; PlanetSystem( &lt;br/&gt;      &lt;span class="hljs-string"&gt;"Alpha Centauri"&lt;/span&gt;)), &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; SpaceShip(&lt;span class="hljs-string"&gt;"Falcon"&lt;/span&gt;, &lt;span class="hljs-literal"&gt;null&lt;/span&gt;) &lt;br/&gt;  }; &lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-keyword"&gt;foreach&lt;/span&gt;(&lt;span class="hljs-keyword"&gt;var&lt;/span&gt; spaceShip &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; spaceShips) &lt;br/&gt;    Console.WriteLine(GetLanguage(spaceShip)); &lt;br/&gt;  } &lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; &lt;span class="hljs-keyword"&gt;string&lt;/span&gt; &lt;span class="hljs-title"&gt;GetLanguage&lt;/span&gt;(&lt;span class="hljs-params"&gt; &lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;    SpaceShip spaceShip&lt;/span&gt;) &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;  &lt;/span&gt;{ &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; spaceShip &lt;span class="hljs-keyword"&gt;switch&lt;/span&gt; &lt;br/&gt;    { &lt;br/&gt;      SpaceShip { PlanetSystem: { &lt;br/&gt;        star: &lt;span class="hljs-string"&gt;"Epsilon Eridani"&lt;/span&gt; } } =&amp;gt; &lt;span class="hljs-string"&gt;"German"&lt;/span&gt;, &lt;br/&gt;      SpaceShip { PlanetSystem: { &lt;br/&gt;        star: &lt;span class="hljs-string"&gt;"Alpha Centauri"&lt;/span&gt; } } =&amp;gt; &lt;span class="hljs-string"&gt;"Klingon"&lt;/span&gt;, &lt;br/&gt;      _ =&amp;gt; &lt;span class="hljs-string"&gt;"unknown"&lt;/span&gt; &lt;br/&gt;    }; &lt;br/&gt;  } &lt;br/&gt;&lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; record &lt;span class="hljs-title"&gt;SpaceShip&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;span class="hljs-keyword"&gt;string&lt;/span&gt; name, &lt;/span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-function"&gt;&lt;span class="hljs-params"&gt;    PlanetSystem PlanetSystem&lt;/span&gt;)&lt;/span&gt;; &lt;br/&gt;  &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; record &lt;span class="hljs-title"&gt;PlanetSystem&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;span class="hljs-keyword"&gt;string&lt;/span&gt; star&lt;/span&gt;)&lt;/span&gt;;  
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)
&lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; string GetLanguage(&lt;br/&gt;  SpaceShip spaceShip) &lt;br/&gt;{ &lt;br/&gt;  &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; spaceShip &lt;span class="hljs-keyword"&gt;switch&lt;/span&gt; &lt;br/&gt;  { &lt;br/&gt;    {PlanetSystem.star: &lt;span class="hljs-string"&gt;"Epsilon Eridani"&lt;/span&gt; } =&amp;gt; &lt;br/&gt;      &lt;span class="hljs-string"&gt;"German"&lt;/span&gt;, &lt;br/&gt;    {PlanetSystem.star: &lt;span class="hljs-string"&gt;"Alpha Centauri"&lt;/span&gt; } =&amp;gt; &lt;br/&gt;      &lt;span class="hljs-string"&gt;"Klingon"&lt;/span&gt;, &lt;br/&gt;     _ =&amp;gt; &lt;span class="hljs-string"&gt;"unknown"&lt;/span&gt; &lt;br/&gt;   }; &lt;br/&gt;  }  
Zu erwähnen ist, dass die definierten Switch-Cases nicht zutreffen und der default-Wert verwendet wird (hier un­known), 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 Va­riante 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 inter­pretieren, 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 &lt; 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">"-&gt; {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&lt;int&gt; {<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 =&gt; 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> =&gt;</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>&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">int</span>&gt;(); 
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>&lt;int&gt; { 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&lt;User&gt; { 
  <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 =&gt; x.age)); 
Console.WriteLine(users.MaxBy(x =&gt; 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 Neue­rungen mit, die das Programmieren nochmals spürbar bequemer machen. Die neuen Features in Kombination mit Visual Studio 2022 hinterlassen einen erstklassigen Eindruck.

Fussnoten

  1. Tiobe Index, http://www.tiobe.com/tiobe-index
  2. Global-Using-Direktiven, http://www.dotnetpro.de/SL2203Csharp101
  3. DateOnly und TimeOnly, http://www.dotnetpro.de/SL2203Csharp102
  4. DateOnly und TimeOnly, http://www.dotnetpro.de/SL2203Csharp103
  5. Null argument checks, http://www.dotnetpro.de/SL2203Csharp104
  6. Jetbrains, Caller Argument Expressions, http://www.dotnetpro.de/SL2203Csharp105
  7. Microsoft, Caller Argument Expressions, http://www.dotnetpro.de/SL2203Csharp106

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
IoT neu eingebunden - Integration und Verwaltung von IoT-Geräten mit Azure IoT Operations
Wie sich das neue Azure IoT Operations von bestehenden Azure-Diensten unterscheidet, welche Technologien dabei zum Einsatz kommen und wann sich der Umstieg lohnt.
16 Minuten
15. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Bausteine guter Architektur - Entwurf und Entwicklung wartbarer Softwaresysteme, Teil 2
Code sauberer gestalten anhand von wenigen Patterns und Grundhaltungen.
6 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige