.NET 9.0 Preview 6 bringt einige lang ersehnte Funktionen

Die sechste Vorschauversion von .NET 9.0 bringt unter anderem endlich partielle Properties. Neu ist zudem die generische Mengenklasse OrderedDictionary<T,T>.

In Pocket speichern vorlesen Druckansicht 1 Kommentar lesen
.NET-Bild

(Bild: Pincasso/Shutterstock.com)

Lesezeit: 11 Min.
Von
  • Dr. Holger Schwichtenberg
Inhaltsverzeichnis

Microsoft hat in der Nacht von Dienstag auf Mittwoch die sechste Vorschauversion von .NET 9.0 zum Download bereitgestellt. Für die Entwicklung mit .NET 9.0 Preview 6 benötigt man die aktuelle Preview-Version von Visual Studio 2022 17.11, die gleichzeitig auf den Stand "Preview 3" aktualisiert wurde. Alternativ dazu kann man Visual Studio Code mit C# Dev Kit einsetzen.

Die wichtigsten Neuerungen in .NET 9.0 Preview 6 sind partielle Properties für die dreizehnte Sprachversion von C#, die zusammen mit .NET 9.0 ausgeliefert wird. Auf dieses Sprachfeature warten viele bereits seit der Einführung der partiellen Methoden in C# 3.0. Das Schlüsselwort partial gibt es sogar bereits seit C# 2.0 für Klassen.

Mit partiellen Klassen kann man den Programmcode einer einzigen Klasse auf mehrere Dateien aufspalten – ohne dafür Vererbung zu nutzen. Das ist nicht nur für mehr Übersichtlichkeit bei umfangreichen Klassen sinnvoll, sondern wird vor allem verwendet, wenn ein Teil der Klasse automatisch generiert und der andere Teil der Klasse manuell geschrieben wird. Diese Vorgehensweise kommt in .NET zum Beispiel bei GUI-Bibliotheken wie ASP.NET Webforms und Blazor, beim Reverse Engineering von Datenbanken mit Entity Framework und Entity Framework Core sowie bei Source-Generatoren unter anderem für reguläre Ausdrücke und JSON-Serialisierung zum Einsatz.

In C# 13.0 können Entwicklerinnen und Entwickler auch Property-Definitionen und deren Implementierung in zwei Dateien trennen. Dabei müssen beide Teile jeweils die gleiche Kombination von Getter und Setter mit den gleichen Sichtbarkeiten umsetzen. Konkretes Beispiel: Wenn in einem Teil der Klasse eine Property sowohl einen öffentlichen Getter als auch einen öffentlichen Setter besitzt, müssen diese auch im anderen Teil vorhanden und öffentlich sein. Aber während in einem Teil eine automatische Property verwendet wird, kann im anderen Teil eine explizite Implementierung vorhanden sein. Die folgenden Codeausschnitte zeigen ein Beispiel einer aufgeteilten Klasse mit partieller Methode und partieller Property.

Der erste Teil der Klasse nur mit Definitionen von ID und Print() verwendet eine automatische Property:

namespace NET9_Console.CS13;

public partial class PersonWithAutoID
{
  public partial int ID { get; set; }
  public string Name { get; set; }

  public partial void Print();
}

Im zweiten Teil der Klasse werden Getter und Setter für ID sowie die Methode Print() explizit implementiert:

namespace NET9_Console.CS13;

public partial class PersonWithAutoID
{
 int counter = 0;

 private int iD;

 public partial int ID
 {
  get
  {
   if (iD == 0) iD = ++counter;
   return iD;
  }
  set
  {
   if (ID>0) throw new ApplicationException("ID ist bereits gesetzt");
   iD = value;
  }
 }

 public partial void Print()
 {
  Console.WriteLine($"{this.ID}: {this.Name}");
 }
}

Folgender Code nutzt die zusammengesetzte Klasse PersonWithAutoID

public class CS13_PartialPropertyDemoClient
{
 public void Run()
 {
  CS13.PersonWithAutoID p = new() { Name = "Holger Schwichtenberg" };
  p.Print(); // 1: Holger Schwichtenberg
  try
  {
   p.ID = 42;
  }
  catch (Exception ex)
  {
   CUI.Error(ex); // System.ApplicationException: ID ist bereits gesetzt
  }
 }
}

Microsoft setzt dieses neue Sprachfeature selbst ein und erlaubt in der aktuellen Preview 6 im Source-Generator für reguläre Ausdrücke, dass man die Annotation [GeneratedRegex] nun nicht mehr nur für Methoden, sondern auch Properties verwenden kann. Das folgende Codebeispiel zum Validieren von E-Mail-Adressen verwendet die Neuerung:

public partial class Checker_Alt // Partielle Klasse
{
 [GeneratedRegex(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
 // Partielle Methode, die dann von SG implementiert wird
 public partial Regex EMailRegEx();
}

public partial class Checker_Neu // Partielle Klasse
{
 [GeneratedRegex(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
 // Partielle Property mit Getter, der dann von SG implementiert wird
 public partial Regex EMailRegEx { get; }
}

public class FCL9_RegExSourceGenerator
{
 public void Run()
 {
  CUI.Demo(nameof(FCL9_RegExSourceGenerator));
  // Aufruf der partiellen Methode
  Console.WriteLine(new Checker_Alt().EMailRegEx().IsMatch("max@mustermann.de"));
  // Aufruf der partiellen Property
  Console.WriteLine(new Checker_Neu().EMailRegEx.IsMatch("max@mustermann.de"));
 }
}

Der in .NET 7.0 eingeführte Source-Generator erzeugt beim Kompilieren zur Entwicklungszeit den Inhalt der Methode und den Getter der partiellen Property. Den generierten Programmcode findet man in der Datei RegexGenerator.g.cs im Ordner C:\Users\Benutzername\AppData\Local\Temp\VSGeneratedDocuments\GUID.

Online-Konferenz zu .NET 9.0 am 19. November

(Bild: Dmytro Vikarchuk/Shutterstock))

In der Online-Konferenz betterCode() .NET 9.0 am 19. November 2024 von iX und dpunkt.verlag präsentieren .NET-Experten von www.IT-Visions.de den fertigen Stand von .NET 9.0 anhand von Praxisbeispielen. Dazu zählen die Neuerungen in .NET 9.0 SDK, C# 13.0, ASP.NET Core 9.0, Blazor 9.0, Windows Forms 9.0, WPF 9.0, WinUI, .NET MAUI 9.0 und die Integration von Künstlicher Intelligenz in .NET-Anwendungen. Das Programm bietet sechs Vorträge, eine Diskussion und sechs Workshops.

Tickets sind zum Frühbucherpreis erhältlich.

Seit Langem gibt es in .NET die Klasse System.Collections.Specialized.OrderedDictionary zum Abspeichern von Name-Wert-Paaren, um schnell ein Element anhand des Schlüssels zu finden. Die Add()-Methode erwartet dabei zwei beliebige .NET-Objekte. In .NET 9.0 führt Microsoft endlich auch eine generische Variante von OrderedDictionary<T,T> ein, mit der man den Typ für Schlüssel und Wert festzurren kann:

public void GenericOrderedDictionary()
 {
  CUI.Demo(nameof(GenericOrderedDictionary));

  OrderedDictionary<string, int> d = new()
  {
   ["www.IT-Visions.de"] = 1996,
   ["www.dotnet7.de"] = 2022,
   ["www.dotnet8.de"] = 2023,
   ["www.dotnet-lexikon.de"] = 2000,
  };

  d.Add("www.dotnet9.de", 2024);
  d.RemoveAt(1);
  d.Insert(0, "www.dotnetframework.de", 2000);

  foreach (KeyValuePair<string, int> entry in d)
  {
   Console.WriteLine(entry);
  }
 }

Das Listing liefert folgende Ausgabe:

[www.IT-Visions.de, 1996]
[www.dotnet8.de, 2023]
[www.dotnet-lexikon.de, 2000]
[www.dotnet9.de, 2024]

.NET 9.0 bietet seit Preview 6 eine weitere neue Mengenklasse: ReadOnlySet<T>. Wie bei Sets üblich, sind keine Duplikate erlaubt. Anders als HashSet<T> lässt sich der Inhalt von ReadOnlySet<T> nicht verändern.

Zur Konstruktion von ReadOnlySet<T> ist eine Menge in Form eines Objekts mit der Schnittstelle ISet<T> erforderlich, beispielsweise HashSet<T> wie im nächsten Codebeispiel. ReadOnlySet<T> vervollständigt die Nur-Lese-Mengen in .NET. Davor gab es bereits ReadOnlyCollection<T> für alles, was IList<T> anbietet, und ReadOnlyDictionary<TKey, TValue> für IDictionary<TKey, TValue>:

public void ReadOnlySet()
{
 CUI.Demo(nameof(ReadOnlySet));

 CUI.H1("HashSet<T> erlaubt Hinzufügen/Löschen");

 HashSet<string> set1 = new()
 {
  // alle obigen URLs hinzufügen
  "www.IT-Visions.de",
  "www.dotnet7.de",
  "www.dotnet8.de",
  "www.dotnet9.de",
  "www.dotnettraining.de",
 };

 var r = set1.Add("www.dotnet9.de");
 Console.WriteLine(r ? "URL neu" : "URL schon vorhanden");
 set1.Remove("www.dotnet7.de");

 foreach (var url in set1)
 {
  Console.WriteLine(url);
 }

 CUI.H1("ReadOnlySet<T> erlaubt KEIN Hinzufügen/Löschen");
 ReadOnlySet<string> set2 = new(set1);

 //set.Add("www.dotnet9.de"); // nicht erlaubt!
 //set.Remove("www.dotnet7.de"); // nicht erlaubt!
 foreach (var url in set1)
 {
  Console.WriteLine(url);
 }

}