.NET-10.0-Vorschau erweitert Klassenbibliotheken

Die erste Preview-Version der zehnten .NET-Version enthält eine Reihe von Neuerungen in den Klassenbibliotheken sowie neue Sprachfeatures in C# 14.0.

vorlesen Druckansicht

(Bild: Pincasso/Shutterstock.com)

Lesezeit: 12 Min.
Von
  • Dr. Holger Schwichtenberg
Inhaltsverzeichnis
close notice

This article is also available in English. It was translated with technical assistance and editorially reviewed before publication.

Zwei Monate nach dem Erscheinen von .NET 9.0 beginnt Microsoft die Vorschaureihe auf .NET 10.0. Die erste Preview-Version steht im .NET-Downloadbereich bei Microsoft bereit (siehe Abbildung 1).

Dr. Holger Schwichtenberg
Dr. Holger Schwichtenberg

Dr. Holger Schwichtenberg hat Fachbücher zu .NET 10.0, C# 14.0, Blazor 10.0 und Entity Framework Core 10.0 veröffentlicht. Er arbeitet als Berater und Trainer bei www.IT-Visions.de.

Diese Version wird nicht mit den zeitgleich erschienenen Updates von Visual Studio 2022 (siehe Abbildung 2) installiert.

Der Installationsbildschirm von .NET 10.0 meldet die erfolgreiche Einrichtung (Abb. 1).

(Bild: Screenshot (Holger Schwichtenberg))

Das aktuelle Update zu Visual Studio installiert nicht automatisch die erste Preview von .NET 10.0 (Abb. 2).

(Bild: Screenshot (Holger Schwichtenberg))

Anwendungen mit .NET 10.0 Preview 1 kompilieren, aber sowohl in Visual Studio 2022 17.13.1 als auch Visual Studio 2022 17.14.0 Preview 1.1. Beim Anlegen eines Projekts in Visual Studio 2022 17.13.1 steht .NET 10.0 allerdings nicht zur Auswahl. Hier muss man manuell im Projekt <TargetFramework>net10.0</TargetFramework> setzen. Visual Studio 2022 17.14.0 Preview 1.1 kennt hingegen .NET 10.0 bereits.

Wie in den letzten beiden .NET-Versionen auch, liefert Microsoft wieder neue Operatoren für Language Integrated Query (LINQ). Dieses Mal sind es mit LeftJoin() und RightJoin() zwei elementare Operatoren aus der Mengenlehre und den relationalen Datenbanken. Tatsächlich waren diese Operationen bisher in LINQ schon möglich, allerdings umständlich mit Hilfe einer Gruppierung und DefaultIfEmpty(). Die neuen Methoden LeftJoin() und RightJoin() vereinfachen den Einsatz:

CUI.H2("--- LeftJoin ALT seit .NET Framework 3.5 ---");

var AllCompaniesWithWebsitesSetOld = outer
  .GroupJoin(inner,
             c => c.ID,
             w => w.CompanyID,
             (c, websites) => new { Company = c, Websites = websites })
  .SelectMany(
    x => x.Websites.DefaultIfEmpty(),  // Falls keine Webseite existiert, wird `null` verwendet
    (c, w) => new WebsiteWithCompany
    {
      Name = c.Company.Name,
      URL = w.URL,   // Falls `w` null ist, bleibt URL null
      City = c.Company.City
    });

foreach (var item in AllCompaniesWithWebsitesSetOld)
{
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : 
    "- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}

CUI.H2("--- LeftJoin NEU ab .NET 10.0 ---");
var AllCompaniesWithWebsitesSet = outer.LeftJoin(inner, e => e.ID, e => e.CompanyID, (c, w) 
  => new WebsiteWithCompany { Name = c.Name, City = c.City, URL = w.URL });
foreach (var item in AllCompaniesWithWebsitesSet)
{
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City :
    "- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}

CUI.H2("--- RightJoin OLD seit .NET Framework 3.5  ---");
var WebsiteWithCompanySetOLD = inner
  .GroupJoin(outer,
             w => w.CompanyID,
             c => c.ID,
             (w, companies) => new { Website = w, Companies = companies })
  .SelectMany(
    x => x.Companies.DefaultIfEmpty(),  // Falls kein Unternehmen existiert, bleibt `null`
    (w, c) => new WebsiteWithCompany
    {
      Name = c.Name,  // Falls `c` null ist, bleibt `Name` null
      City = c.City,   // Falls `c` null ist, bleibt `City` null
      URL = w.Website.URL
    });

foreach (var item in WebsiteWithCompanySetOLD)
{
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : 
    "- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}

CUI.H2("--- RightJoin NEU ab .NET 10.0 ---");
var WebsiteWithCompanySet = outer.RightJoin(inner, e => e.ID, e => e.CompanyID, (c, w) 
  => new WebsiteWithCompany { Name = c.Name, City = c.City, URL = w.URL });

foreach (var item in WebsiteWithCompanySet)
{
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City :
    "- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}

// Zum Vergleich: Inner Join, den es seit .NET Framework 3.5 gibt
CUI.H2("--- InnerJoin seit .NET Framework 3.5 ---");
var CompaniesWithWebsitesSet = outer.Join(inner,
                                          c => c.ID,
                                          w => w.CompanyID,
                                          (c, w) => new WebsiteWithCompany
                                          {
                                            Name = c.Name,
                                            URL = w.URL,
                                            City = c.City
                                          });
foreach (var item in CompaniesWithWebsitesSet)
{
  Console.WriteLine((item.Name != null ? item.Name + " " + item.City : 
    "- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}

Die neuen Operatoren LeftJoin() und RightJoin() werden auch in Entity Framework Core 10.0 fĂĽr Datenbankzugriffe unterstĂĽtzt, siehe die Issues "Support the new .NET 10 LeftJoin operators" und "Fully support right outer joins".

Die in .NET 9.0 neu eingefĂĽhrte generische Klasse System.Collections.Generic.OrderedDictionary<T,T> bot bisher schon eine Methode TryAdd(), die versucht, ein Element hinzuzufĂĽgen. Neben der bestehenden Variante TryAdd(TKey key, TValue value) gibt es nun auch TryAdd(TKey key, TValue value, out int index). Diese neue Ăśberladung liefert den Index zurĂĽck, falls es das Element in der Menge schon gibt. Analog dazu gibt es TryGetValue() nun mit einer Ăśberladung, die nicht nur den Wert eines Eintrags liefert, sondern auch die Position per Index:

OrderedDictionary<string, string> websites = new OrderedDictionary<string, string>();
websites.Add("Heise", "www.Heise.de");
websites.Add("Microsoft", "www.Microsoft.com");
websites.Add("IT-Visions", "www.IT-Visions.de");

var propertyName = "IT-Visions";
var value = "www.IT-Visions.de";

// bisher
if (!websites.TryAdd(propertyName, value))
{
  int index1 = websites.IndexOf(propertyName); // Second lookup operation
  CUI.Warning("Element " + value + " ist bereits vorhanden!");
}

// neu
if (!websites.TryAdd(propertyName, value, out int index))
{
  CUI.Warning("Element " + value + 
              " ist bereits vorhanden an der Position " + 
              index + """!""");
}

// neu
if (websites.TryGetValue(propertyName, out string? value2, out int index2))
{
  CUI.Success($"Element {value2} wurde gefunden an der Position {index2}.");
}

Zur PrĂĽfung von IP-Adressen gibt es bisher die statische Methode IPAddress.TryParse(), die eine IP-Adresse aus einer Zeichenkette oder

den Typen ReadOnlySpan<char> beziehungsweise ReadOnlySpan<byte> extrahiert. Der RĂĽckgabewert in bool und die extrahierte IP-Adresse wird in Form einer Instanz der Klasse IPAddress als out-Parameter geliefert. Wenn man nur prĂĽfen will, ob die IP-Adresse stimmt, schreibt man IPAddress.TryParse(eingabe, out _).

In .NET 10.0 bietet Microsoft eine weitere Variante mit weniger internem Aufwand in der statischen Methode IsValid(), beispielsweise System.Net.IPAddress.IsValid("192.168.1.0"). Dabei kehrt Microsoft nun einfach eine bisher interne Methode TargetHostNameHelper.IsValidAddress() nach auĂźen.

In der JSON-Bibliothek hat Microsoft die Klasse JsonElement um die Methode GetPropertyCount() erweitert, mit der man ermitteln kann, wie viele Eigenschaften ein JSON-Objekt besitzt:

JsonElement element1 = JsonSerializer.Deserialize<JsonElement>("""{ "ID" : 1, "Name" : "Dr. Holger Schwichtenberg", "Website": "www.IT-Visions.de" }""");
Console.WriteLine(element1.GetPropertyCount()); // 3

Bisher war nur möglich, die Anzahl der Objekte in einem Array zu ermitteln:

JsonElement element2 = JsonSerializer.Deserialize<JsonElement>("""[ 1, 2, 3, 4 ]""");
Console.WriteLine(element2.GetArrayLength()); // 4

In der Klasse JsonArray im Namensraum System.Text.Json.Nodes bietet Microsoft die beiden neuen Methoden RemoveRange() und RemoveAll(). RemoveRange() nimmt zwei Zahlen entgegen für Index und Count. Der Index ist wie üblich nullbasiert: array.RemoveRange(5, 2) entfernt das sechste und siebte Element. RemoveAll() erwartet ein Predicate<JsonNode>-Objekt und erlaubt die Angabe eines Löschkriteriums. Beispielsweise array.RemoveAll(n => n.GetValue<int>() > 5):

System.Text.Json.Nodes.JsonArray array = JsonSerializer.Deserialize<System.Text.Json.Nodes.JsonArray>("""[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]""");
System.Console.WriteLine(array.Count); // 9
PrintJsonArray(array);

array.RemoveRange(5, 2);
PrintJsonArray(array);

array.RemoveAll(n => n.GetValue<int>() > 5);
PrintJsonArray(array);

AuĂźerdem hat Microsoft Folgendes in .NET 10.0 Preview 1 implementiert:

  • In der Klasse X509Certificate2Collection exsitiert die neue Methode FindByThumbprint(), um ein Zertifikat anhand des SHA-Fingerabdrucks zu finden
  • Die Klasse ISOWeek bietet drei neue Methoden zum Umgang mit dem Datentyp DateOnly. Bisher war hier nur System.DateTime möglich:
  • Die Implementierung der Verarbeitung von ZIP-Archiven wurde optimiert. Unter anderem wurde laut Microsoft das Aktualisieren von ZIP-Archiven um 99,8 % beschleunigt (ein HinzufĂĽgen einer 2-GB-Datei von 177,7 ms auf 0,13 ms) und verbraucht nun 99,99 % weniger RAM (nun 7,01 KB statt 2 GB RAM).
  • Die Annotation [JsonSourceGenerationOptions] fĂĽr den Source-Generator in der Bibliothek Text.Json erlaubt nun auch die Einstellung des Verhaltens bei zirkulären Referenzen auf Unspecified, Preserve oder IgnoreCycles
  • In Windows Forms sind nun einige Ăśberladungen der GetData()-Methode fĂĽr die Zwischenablage sowie Drag-and-Drop-Operationen als "obsolet" markiert, die noch den BinaryFormatter verwenden, den Microsoft in .NET 9.0 ausgebaut hat und fĂĽr den man nun ein eigenes NuGet-Paket benötigt. DafĂĽr gibt es nun neue Operationen, die mit JSON arbeiten, darunter SetDataAsJson() und TryGetData().

In WPF gibt es laut Release Notes nur Qualitätsverbesserungen. In .NET MAUI 10.0 gibt es neue Einstellungen. Zudem können Android-Projekte nun mit dotnet run von der Kommandozeile direkt gestartet werden. Im .NET SDK gibt es eine Optimierung zum Beschleunigen des Package Restore.