14. Okt 2024
Lesedauer 7 Min.
.NET 9 in den finalen Zügen
Neuerungen in .NET 9
Wie jedes Jahr präsentiert Microsoft auch im November 2024 auf der .NET Conf, was es Neues in der .NET-Welt geben wird. Ein Vorab-Blick auf .NET 9.

Stand heute (Ende August) wurde gerade die letzte Vorschauversion von .NET 9 vorgestellt, und im September und Oktober werden noch die finalen Release Candidates mit den letzten Qualitätsupdates veröffentlicht. Doch was kommt nun mit .NET 9 tatsächlich auf uns zu?
Die Kommunikation mit Microsoft hat sich geändert
Früher – vor .NET 9 – war das .NET-Blog die erste Anlaufstelle für alle Änderungen, die in die neue Version aufgenommen werden. Mit .NET 9 hat Microsoft verstärkt auf GitHub Discussions gesetzt und dort den Verlauf der Änderungen von .NET 9 dokumentiert. Ob nun die Runtime, die Library oder auch EF Core 9 oder ASP.NET Core 9: Man wird dort mit all den Issues konfrontiert, die gelöst worden sind [1]. Auch auf der gewöhnlichen Learn-Plattform fasst Microsoft knapp und einfach die wichtigsten Punkte zusammen [2].Der BinaryFormatter ist tot
Wie bereits seit Jahren geplant und angekündigt, ist es mit .NET 9 so weit – der BinaryFormatter ist endgültig nicht mehr vorhanden. Bei der Verwendung ist der Datentyp selbst komplett als „obsolete“ markiert (Bild 1). Dies war auch schon früher der Fall; nun ist es aber so, dass der BinaryFormatter nicht nur eine Warnung ausgibt, sondern das Laufzeitverhalten ein gänzlich anderes ist.
Obsoleter BinaryFormatter (Bild 1)
Autor
Der BinaryFormatter ist im Typsystem noch komplett erhalten geblieben, und es scheint somit auf den ersten Blick, als sei er noch verwendbar. Nachdem man mithilfe eines Pragmas die Warnung SYSLIB0011 deaktiviert hat, kann man den Compiler auch dazu zwingen, den BinaryFormatter-Datentyp zu verwenden. Wird nun aber auf eine Methode dieses Datentyps zugegriffen, resultiert dies zur Laufzeit nur noch in einer PlatformNotSupportedException(Bild 2).

BinaryFormatter ist nun Geschichte (Bild 2)
Autor
Wer also noch immer auf den BinaryFormatter setzt, sollte sich nun langsam, aber sicher nach Alternativen umschauen. Grund für diese Änderung waren unter anderem Sicherheitslücken in der Art der Serialisierung (siehe dazu meinen Artikel aus der dotnetpro-Ausgabe 12/2023 unter [3], aber auch [4]). Theoretisch gibt es allerdings noch eine Hintertür, die Microsoft liefert, und zwar kann der BinaryFormatter über ein spezielles NuGet-Paket [5] wieder nachgerüstet werden – damit wird die „alte“ Implementierung wieder aktiviert, und selbst unter .NET 9 können wir damit immer noch den BinaryFormatter nutzen.Dies ist eine Änderung, die Microsoft langjährig verfolgt hat und die nun endlich am Ende angekommen ist. Fraglich ist an dieser Stelle, ob man den Datentyp mit .NET 10 dann endlich wirklich entfernt, oder ob uns die PlatformNotSupportedException noch die nächsten Jahre oder Jahrzehnte begleiten wird.
Die JSON-Standardweboptionen
Neu mit .NET 9 ist beim JsonSerializer eine Erweiterung um die JsonSerializerOptions.Web-Eigenschaft (Bild 3). Diese Eigenschaft kann beim Serialisieren sowie Deserialisieren genutzt werden. Diese Einstellungen sind deckungsgleich mit den Einstellungen, die Microsoft bei ASP.NET Core in der Vergangenheit bereits genutzt hat, und sind somit vor allem für Bibliotheken relevant, die nicht im ASP.NET-Core-Umfeld liegen – Bild 4 zeigt dabei die unterschiedlichen Werte, die man üblicherweise manuell setzen musste, doch mit .NET 9 eben komfortabel über eine zentrale Eigenschaft lösen kann.
JSON-Standardweboptionen (Bild 3)
Autor

Die unterschiedlichen Werte im Vergleich zu „Default“ (Bild 4)
Autor
LINQ hat neue Operationen
Unser geliebtes LINQ hat wieder neue Operationen verpasst bekommen und damit wieder neue Funktionen für uns freigegeben, die wir in speziellen Fällen nutzen können. Konkret geht es dabei um die Methoden CountBy und AggregateBy. Am Ende sind diese beiden Methoden nur Abkürzungen zu der bisherigen GroupBy-Variante und kürzen somit hier die Schreibweisen etwas ab.Dabei ist der große Unterschied, dass man eben eine Menge erhält, bei der einerseits der „Gruppierungsschlüssel“ und andererseits der Wert direkt zurückgeliefert werden. Dies war mittels eines GroupBy gefolgt von einem Select auch vorher schon technisch möglich, ist damit nun aber etwas leichter und kürzer geworden (Bild 5).
Ausgabe der Ergebnismenge mit Anzahl (Bild 5)
Autor
Eine ebenfalls etwas kurios anmutende Umsetzung ist die neue Methode Index. Schon immer war es ein Problem von LINQ, dass die Indexermittlung in der Vergangenheit recht eigenartig war. Entweder man merkte sich einen separaten integer und erhöhte diesen bei jedem Schleifendurchlauf, oder man wechselte auf eine gute, altbewährte for-Schleife (Bild 6).

Indexermittlung mit foreach - simpel! (Bild 6)
Autor
Neuartig ist nun die Methode Index, die ein ValueTuple mit dem Eintrag und ebenfalls den Index des Eintrags zurückgibt. So klein diese Änderung auch sein mag – ab und an bietet sie einen gewissen Komfort.
Persistenter AssemblyBuilder
Eine letzte „Kleinigkeit“ ging dem .NET-Core-Ökosystem noch ab, insbesondere wenn man vom .NET Framework migrierte oder dies noch vor sich hat – persistente AssemblyBuilder (Bild 7).
Dynamische Assemblies zur Laufzeit bauen (Bild 7)
Autor
Dabei kann man mittels .NET und C# bereits seit den Anfängen des .NET Frameworks zur Laufzeit IL-Code erzeugen und diesen dynamisch laden. Insbesondere dynamische Frameworks wie Entity Framework Core oder auch Castle Windsor setzen recht stark auf solche Features, um performanten Code zu ermöglichen.Was bisher in .NET nicht möglich war, ist, dass dieser dynamische Code abgespeichert und zum Beispiel auf die Festplatte geschrieben wird. Dies wurde nun mit .NET 9 behoben, und man erhält Zugriff auf die neue Methode AssemblyBuilder.DefinePersistedAssembly, mit deren Hilfe man die Assembly persistieren kann, um sie später oder auch ein anderes Mal zu laden.
Performance
Beim Release von .NET 9 wird Microsoft wieder alles auffahren, was die Marketingabteilung so hergibt. Insbesondere wird mit Sicherheit wieder erwähnt werden, dass .NET 9 das schnellste .NET ist und um soundsoviel Prozent besser performt als .NET 8. Natürlich stimmt daran einiges, gerade beim Blick auf die Neuerungen in der Runtime selbst. Mit JIT-Optimierungen, Schleifenoptimierungen und GC-Optimierungen wird .NET 9 (je nach Situation) bestimmt schneller sein [6]. Doch eines soll an dieser Stelle herausgehoben werden: Microsoft hat Exceptions beschleunigt.Mit .NET 9 hat Microsoft den Weg, wie Microsoft mit Exceptions umgeht, grundlegend geändert. War es bisher so, dass .NET noch die Unterstützung des „Windows structured exception handling“ einschloss, ist diese nun entfernt worden. Daraus ergibt sich, dass die Performance beim Exception-Handling mit .NET 9 ungefähr um den Faktor zwei bis vier verbessert werden konnte – ein beeindruckender Wert.Neue Methoden bei „TimeSpan“
Eine weitere „Kleinigkeit“ hat Microsoft nun geradegezogen: Mithilfe der Factory-Methoden können auf dem Datentyp TimeSpan beliebige Methoden genutzt werden, um einen TimeSpan zu erzeugen. Bis dato war dies nur mit einem double möglich.Neu ist nun mit .NET 9, dass auch mit einem int oder long gearbeitet werden kann. Hintergrund für diese Änderungen war, dass es zu Rundungsfehlern kommen konnte, wenn ein int in einen double abgeändert wird. Deswegen hat Microsoft den Datentyp dahingehend angepasst, dass er eben auch Ganzzahlen entgegennehmen kann (Bild 8 und Bild 9).
TimeSpan-Methoden bei .NET 8 (Bild 8)
Autor

Im Vergleich dazu – mit .NET 9 (Bild 9)
Autor
.NET Standard wird immer „unwichtiger“
Ebenfalls mit .NET 9 kommt der eine oder andere „Breaking Change“ [7]. Zu diesen zählt zum Beispiel, dass .NET Standard 1.x nicht mehr als „aktuell“ gilt.Kompiliert man eine Bibliothek nun gegen .NET Standard 1.x mit dem .NET-9-SDK, so wird eine Compilerwarnung ausgegeben: „Warning NETSDK1215: Targeting .NET Standard prior to 2.0 is no longer recommended.“ Dies deutet auch immer mehr in die Richtung, dass mittlerweile der eigentliche Grund für .NET Standard – die Migration von .NET Framework zu .NET – aus Sicht von Microsoft langsam, aber sicher als abgeschlossen gilt.Fazit
.NET 9 – eine bemerkenswerte Version? Wohl kaum. Gerade im Hinblick auf die Features, die in .NET enthalten sind, als auch jene in ASP.NET Core (siehe in dieser dotnetpro-Ausgabe ab Seite 10 [8]) oder EF Core (siehe ab Seite 108 [9]) sind sicherlich einige nützliche „Kleinigkeiten“ dabei, aber die großen Features und echten Innovationen sind etwas hinter den (persönlichen) Erwartungen zurückgeblieben. Vor allem wurden Features erneut verschoben oder auch versprochen und dann verschoben – sehr zum Bedauern der Community.Am Ende bleibt das jedoch Meckern auf hohem Niveau: Schließlich gibt es bei Microsoft schon lange das Versprechen, uns in der agilen Form ein Framework zu liefern, auf das Verlass ist. Und das hält Microsoft seither auch ein.Fussnoten
- GitHub Discussions, .NET 9 Release Index #9234, http://www.dotnetpro.de/SL2411NET9_1
- Microsoft Learn, Neuerungen in .NET 9, http://www.dotnetpro.de/SL2411NET9_2
- Christian Giesswein, [Serializable] ist tot, dotnetpro 12/2023, Seite 60 f., http://www.dotnetpro.de/A2312NETirol
- Common Weakness Enumeration #502: Deserialization of Untrusted Data, http://www.dotnetpro.de/SL2411NET9_3
- NuGet-Paket System.Runtime.Serialization.Formatters, http://www.dotnetpro.de/SL2411NET9_4
- Microsoft Learn, What’s new in the .NET 9 runtime, http://www.dotnetpro.de/SL2411NET9_5
- Microsoft Learn, Breaking Changes in .NET 9, http://www.dotnetpro.de/SL2411NET9_6
- Christian Wenz, Ausbesserungsarbeiten im Web, dotnetpro 11/2024, Seite 10 ff., http://www.dotnetpro.de/A2411ASPNETCore
- Holger Schwichtenberg, AOT noch in Arbeit, dotnetpro 11/2024, Seite 108 ff., http://www.dotnetpro.de/A2411DataAccess