Juli 2019

Band 34, Nummer 7

[C#]

.NET erneut vereint: Microsofts Pläne für .NET 5

Von Mark Michaelis | Juli 2019

Als Microsoft im Mai auf der Microsoft Build 2019 .NET 5 ankündigte, markierte dies einen wichtigen Schritt nach vorne für Entwickler, die auf Desktop-, Web-, Mobil-, Cloud- und Geräteplattformen arbeiten. Tatsächlich ist .NET 5 das seltene Plattformupdate, das divergierende Frameworks vereint, die Codekomplexität verringert und die plattformübergreifende Reichweite deutlich erhöht.

Dies ist eine ziemlich große Aufgabe. Microsoft schlägt vor, die Quellcodestreams mehrerer wichtiger Frameworks zusammenzuführen: .NET Framework, .NET Core und Xamarin/Mono. Die Bemühungen werden sogar Threads vereinheitlichen, die sich am Anfang um die Jahrtausendwende getrennt haben, und Entwicklern ein Zielframework für ihre Arbeit bieten.

Das Quellcodeflusskonzept in Abbildung 1 zeigt, wie die Zeitachsen für jedes Framework synchronisiert und schließlich im November 2020 zu einem einzigen Thread als .NET 5 zusammengeführt werden. (Beachten Sie, dass .NET Framework in der Abbildung zu .NET FW verkürzt wurde.) Nach der Veröffentlichung wird .NET 5 die Produkte .NET Framework 4.8, Mono 5.0 und .NET Core 3.0 beinhalten.

Quellcodeflusskonzept aus .NET, Mono und Shared Source Initiative in .NET 5
Abbildung 1: Quellcodeflusskonzept aus .NET, Mono und Shared Source Initiative in .NET 5 (klicken Sie, um die Abbildung zu vergrößern)

Zugegebenermaßen ist Abbildung 1 konzeptueller als die Wirklichkeit mit Quellcodeforks (wahrscheinlicher nur Kopiervorgängen) anstelle von -branches und Funktionen (wie Windows Presentation Foundation [WPF] oder Windows Forms), die eher migriert als gemergt werden. Dennoch bietet die Infografik einen einigermaßen transparenten Einblick in die Historie des .NET-Quellcodes und zeigt dessen Entwicklung aus den drei Hauptzweigen bis hin zu .NET 5.

Das Ergebnis dieser Bemühungen ist eine einheitliche Plattform mit dem .NET 5-Framework, das auf allen Plattformen (Desktop, Web, Cloud, Mobile usw.) ausgeführt wird. In Abbildung 2 wird diese Architektur dargestellt.

.NET 5: Eine vereinheitlichte Plattform
Abbildung 2: .NET 5 – Eine vereinheitlichte Plattform

Herkunftsgeschichte

Es ist faszinierend, wie die verschiedenen .NET-Frameworks (etwa Microsofts Shared Source Common Initiative (Rotor), SilverLight, Windows Phone, .NET Core und .NET Framework (nicht aber Mono)) ursprünglich aus dem gleichen Quellcode kompiliert wurden. Mit anderen Worten: Der gesamte Quellcode wurde in einem einzigen Repository verwaltet und von allen .NET-Frameworks gemeinsam verwendet. Auf diese Weise konnte Microsoft sicherstellen, dass APIs, die verschiedenen Frameworks gemeinsam waren, aus dem gleichen Quellcode stammen und die gleichen Signaturen aufweisen. Die einzigen Unterschiede bestanden darin, welche APIs gemeinsam verwendet wurden. (Beachten Sie die Schreibweise von „.NET-Framework“ mit Bindestrich, die sich auf alle .NET-Frameworks im Allgemeinen bezieht, im Gegensatz zu „.NET Framework“ ohne Bindestrich, die sich auf Windows .NET Framework bezieht, beispielsweise auf .NET Framework 4.8.)

Um eine einzige Quelle zu erreichen, die auf verschiedene Frameworks ausgerichtet ist, wurden verschiedene Teilmengentechniken verwendet, beispielsweise eine Vielzahl von #ifdefs. Interessant ist auch, wie ein orthogonaler Satz von Teilmengentechniken (also verschiedene #ifdefs) in den .NET-Quellcode eingearbeitet wurde, um die plattformübergreifende Unterstützung von Rotor zu ermöglichen, die eine schnelle Entwicklung von Silverlight und (viel später) .NET Core aus der gleichen Codebasis ermöglicht.

Während der orthogonale Satz von Teilmengentechniken erhalten bleibt, wird diejenige Teilmengentechnik, die eine plattformübergreifende Kompilierung ermöglicht (die Teilmenge zum Generieren der verschiedenen Frameworks), entfernt. (Ein Beispiel finden Sie im Pull Request unter bit.ly/2WdSzv2, der mehrere veraltete #ifdef-Elemente entfernt.) Der Grund, warum es möglich ist, diese heute zu entfernen, besteht darin, dass innerhalb von Tagen nach der Veröffentlichung von .NET 1.1 der Quellcode von .NET Core und .NET Framework geforkt wurde (eher kopiert). Dies erklärt in der Tat, warum es zwei getrennte .NET-Quellcodewebsites gibt: .NET Framework unter referencesource.microsoft.com und .NET Core unter source.dot.net. Hierbei handelt es sich um zwei separate Codebasen.

Die Entscheidung, die Teilmenge nicht weiter zu verwenden, sondern zu forken, spiegelt das Spannungsverhältnis zwischen der Aufrechterhaltung der Abwärtskompatibilität (eine hohe Priorität für .NET Framework) und der Innovation (der Priorität von .NET Core) wider. Es gab einfach zu viele Fälle, in denen die Aufrechterhaltung der Kompatibilität im Widerspruch zur Korrektur oder Verbesserung der .NET-APIs stand, sodass der Quellcode getrennt werden musste, um beide Ziele erreichen zu können. Die Verwendung von #ifdefs war keine funktionale Möglichkeit mehr, die Frameworks zu trennen, da die APIs in der Tat unterschiedlich und versionsinkompatibel waren.

Im Laufe der Zeit entstand jedoch ein weiterer Konflikt, nämlich die Möglichkeit, dass Entwickler Bibliotheken erstellen können, die in beiden Frameworks erfolgreich ausgeführt werden können. Um dies zu erreichen, wurde ein API-Standard benötigt, um Entwicklern zu garantieren, dass ein Framework einen bestimmten Satz von APIs aufweist, die durch den Standard identifiziert werden. Wenn sie nur die APIs innerhalb des Standards nutzen würden, wäre ihre Bibliothek auf diese Weise frameworkübergreifend kompatibel (die exakt gleiche Assembly könnte unter verschiedenen Frameworks ausgeführt werden – sogar ohne Neukompilierung).

Mit jeder neuen Funktion, die .NET hinzugefügt wurde (z.B. Span<T>), wurde es immer schwieriger, die Abwärtskompatibilität mit älteren Versionen des Frameworks aufrechtzuerhalten. Genauer gesagt, bestand die Herausforderung darin, Konzepte in neuen .NET Standard-Versionen – die wirklich neue .NET Core-Innovationen waren – in .NET Framework zu unterstützen. Darüber hinaus (wenn auch weniger bedeutsam) enthielt jede neue Version des .NET-Standards einen immer größeren Satz von APIs, bis es zu einer Verwaltungslast wurde, die .NET Standard-Kompatibilität zwischen .NET Framework und .NET Core zu erhalten.

Zusammenführung

Die beiden Frameworks sahen sich aufgrund des Standards immer ähnlicher. Als die APIs konsistenter wurden, stellte sich die offensichtliche Frage: Warum führen wir die separaten Codebasen nicht wieder zusammen? Und beginnend mit der Vorschau von .NET Core 3.0 wurde tatsächlich so viel von .NET Framework WPF und der Windows-API ausgewählt und in die.NET Core 3.0-Codebasis integriert, dass genau dies passiert ist. Der Quellcode für .NET Core 3.0 wurde mit der modernen Funktionalität (Desktop, Cloud, Mobile und IoT) in .NET Framework 4.8 identisch.

An diesem Punkt gibt es noch ein großes .NET-Framework, mit dem ich mich noch nicht beschäftigt habe: Mono/Xamarin. Obwohl der Quellcode für Rotor öffentlich verfügbar war, hätte seine Verwendung gegen den Lizenzvertrag verstoßen. Stattdessen begann Mono als separate Greenfield-Entwicklungsarbeit mit der Vision, eine Linux-kompatible Version von .NET zu erstellen. Das Mono-Framework wuchs im Laufe der Zeit weiter, bis das Unternehmen (Ximian) 2003 von Novell übernommen und dann acht Jahre später nach dem Verkauf von Novell an Attachmate geschlossen wurde. Das Ximian-Management hat sich im Mai 2011 schnell unter dem Namen Xamarin neu formiert. Und weniger als zwei Jahre später entwickelte Xamarin eine plattformübergreifende UI-Codebasis, die sowohl unter Android als auch unter iOS ausgeführt werden konnte, und nutzte dabei hinter den Kulissen eine plattformübergreifende Version von Mono, die inzwischen Closed-Source ist.

Im Jahr 2016 erwarb Microsoft Xamarin, um den gesamten Quellcode des .NET-Frameworks unter die Kontrolle eines einzigen Unternehmens zu bringen. Kurz danach wurden Mono und das Xamarin SDK als Open-Source veröffentlicht.

Damit kommen wir zu dem Punkt, an dem wir in der ersten Jahreshälfte 2019 stehen, mit im Wesentlichen zwei wichtigen Codebasen, die weiterentwickelt werden: .NET Core 3.0 und Mono/Xamarain. (Während Microsoft .NET Framework 4.8 unter Windows wahrscheinlich noch länger unterstützen wird, wird .NET Core 3.0 und später .NET 5 diese Version als strategische Plattform für neue Anwendungen in Zukunft verdrängen.) Daneben gibt es .NET Standard und die Vereinheitlichung der APIs im bald veröffentlichten .NET Standard 2.1.

Wieder stellt sich die Frage, ob wir nicht .NET Core 3.0 mit Mono zusammenführen können, wenn die APIs immer näher zusammenrücken? Das sind Bestrebungen, die tatsächlich bereits begonnen haben. Mono besteht bereits heute zu einem Drittel aus Mono-Quellcode, zu einem Drittel aus CoreFx und zu einem Drittel aus .NET Framework-Referenzquellen. Damit war die Voraussetzung geschaffen, dass .NET 5 auf der Microsoft Build 2019 angekündigt werden konnte.

Vorteile von .NET 5

Diese vereinheitlichte Version von .NET 5 unterstützt alle .NET-Anwendungstypen: Xamarin, ASP.NET, IoT und Desktop. Darüber hinaus nutzt sie eine einzige CoreFX-/Basisklassenbibliothek (Base Class Library, BCL), zwei getrennte Laufzeiten und Laufzeitcodebasen (weil es wirklich schwierig ist, zwei Laufzeiten, die sich in wichtigen Punkten unterscheiden sollen, aus einer Quelle zu betreiben) sowie eine einzige Toolkette (wie die dotnet-CLI). Das Ergebnis ist die Einheitlichkeit des Verhaltens, der APIs und der Entwicklerfunktionen. Anstatt beispielsweise drei Implementierungen der System.*-APIs zu haben, wird es einen einzigen Satz von Bibliotheken geben, die auf jeder der verschiedenen Plattformen ausgeführt werden können.

Es entstehen zahlreiche Vorteile durch die Vereinheitlichung von .NET. Die Vereinheitlichung des Frameworks, der Laufzeiten und der Entwicklertoolsets in einer einzigen Codebasis führt zu einer Verringerung der Menge an doppelt vorhandenem Code, den Entwickler (sowohl bei Microsoft als auch in der Community) pflegen und erweitern müssen. Außerdem wird der gesamte .NET 5-Quellcode Open-Source sein, wie wir es heutzutage von Microsoft gewohnt sind.

Mit der Zusammenführung werden viele der Funktionen, die für jedes einzelne Framework exklusiv sind, für alle Plattformen verfügbar sein. Beispielsweise werden CSPROJ-Typen für diese Plattformen im beliebten und einfachen CSPROJ-Dateiformat von .NET Core vereinheitlicht. Ein .NET Framework-Projekttyp kann daher die Vorteile des CSPROJ-Dateiformats von .NET Core nutzen. Obwohl eine Konvertierung in CSPROJ-Dateiformate von .NET Core für Xamarin- und CSPROJ-Dateien von.NET Framework (einschließlich WPF und Windows Forms) erforderlich ist, ähnelt die Aufgabe der Konvertierung von ASP.NET in ASP.NET Core. Glücklicherweise ist dies heutzutage dank Tools wie ConvertProjectToNETCore3 (siehe bit.ly/2W5Lk3D) noch einfacher geworden.

Ein weiterer wesentlicher Unterschied besteht im Laufzeitverhalten von Xamarin und .NET Core/.NET Framework. Ersteres verwendet ein statisches Kompilierungsmodell mit einer AOT-Kompilierung (Ahead-of-Time), die den Quellcode bis hinunter zum nativen Quellcode der Plattform kompiliert. Im Gegensatz dazu verwenden .NET Core und .NET Framework JIT-Kompilierung (Just-in-Time). Glücklicherweise werden mit .NET 5 beide Modelle abhängig vom Projekttypziel unterstützt.

Sie können beispielsweise Ihr .NET 5-Projekt in eine einzige ausführbare Datei kompilieren, die zur Laufzeit den JIT-Compiler (jitter) oder einen nativen Compiler für die Ausführung auf iOS- oder Android-Plattformen verwendet. Die meisten Projekte werden den jitter-Compiler nutzen, aber für iOS ist der gesamte Code AOT. Für clientseitiges Blazor ist die Laufzeit Web Assembly (WASM), und Microsoft beabsichtigt, eine kleine Menge an verwaltetem Code (etwa 100 KB bis 300 KB) ahead-of-time zu kompilieren, während der Rest interpretiert wird. (AOT-Code ist groß, daher stellen die Netzwerkkosten eine Belastung dar.)

In .NET Core 3.0 können Sie in eine einzige ausführbare Datei kompilieren, aber diese ausführbare Datei ist eigentlich eine komprimierte Version aller Dateien, die für die Ausführung zur Laufzeit benötigt werden. Wenn Sie die Datei ausführen, erweitert sie sich zunächst in ein temporäres Verzeichnis und führt dann den Einstiegspunkt der Anwendung aus dem Verzeichnis aus, das alle Dateien enthält. Im Gegensatz dazu erstellt .NET 5 eine echte, einzeln ausführbare Datei, die direkt an Ort und Stelle ausgeführt werden kann.

Ein weiteres bemerkenswertes Feature von .NET 5 ist die Interoperabilität mit Quellcode aus Java und Objective-C (einschließlich Swift). Dies war seit den frühen Releases ein Feature von Xamarin, wird aber nun auf alle .NET 5-Projekte erweitert. Sie können beispielsweise JAR-Dateien in Ihre CSPROJ-Datei aufnehmen und Aufrufe direkt aus Ihrem .NET-Code in Java- oder Objective-C-Code ausführen. (Leider wird Unterstützung für Objective-C wahrscheinlich später als für Java verfügbar sein.) Anzumerken ist, dass die Interoperabilität zwischen .NET 5 und Java/Objective-C nur auf prozessinterne Kommunikation abzielt. Verteilte Kommunikation mit anderen Prozessen auf demselben Computer oder sogar Prozessen auf einem anderen Computer erfordert wahrscheinlich eine Serialisierung in einen REST- oder RPC-basierten verteilten Aufruf.

Was .NET 5 nicht enthält

Obwohl es im .NET 5-Framework eine bedeutende Sammlung von APIs gibt, enthält diese Version nicht alles, was in den letzten 20 Jahren entwickelt wurde. Es ist vernünftig zu erwarten, dass alle in .NET Standard 2.1 identifizierten APIs unterstützt werden, aber einige der „älteren“ APIs (einschließlich Web Forms, WCF-Server (Windows Communication Foundation) und Windows Workflow) werden nicht unterstützt. Diese sind dazu bestimmt, nur in .NET Framework zu verbleiben. Wenn Sie die gleiche Funktionalität innerhalb von .NET 5 erreichen möchten, sollten Sie die Portierung dieser APIs wie folgt in Betracht ziehen:

  • ASP.NET Web Forms => ASP.NET Blazor
  • WCF-Server und Remoting = > gRPC
  • Windows Workflow (WF) => Core WF (github.com/UiPath/corewf)

Die fehlende Unterstützung für WCF-Server ist zweifellos für einige Entwickler enttäuschend. Microsoft hat sich jedoch kürzlich entschieden, die Software unter einer MIT-Open-Source-Lizenz zu veröffentlichen, bei der ihr Schicksal unter der Kontrolle der Community steht (siehe github.com/CoreWCF/CoreWCF/CoreWCF). Es ist noch ein enormer Arbeitsaufwand zu leisten, um eine Veröffentlichung unabhängig von .NET Framework zu erreichen, aber in der Zwischenzeit sind die clientseitigen WCF-APIs verfügbar (siehe github.com/dotnet/wcf).

Eine Absichtserklärung

Auch wenn Microsoft plant, seine Entwicklerframeworks unter .NET 5 zu vereinheitlichen, hat das Unternehmen angekündigt, dass es für seine vereinheitlichten .NET-Releases einen regelmäßigen Rhythmus einführt (siehe Abbildung 3). In Zukunft können Sie erwarten, dass die Versionen mit allgemeiner Verfügbarkeit von .NET im 4. Quartal jedes Jahres veröffentlicht werden. Von diesen Releases wird jede zweite Version ein LTS-Release (Long Term Support) sein, das Microsoft für mindestens drei Jahre oder noch ein Jahr nach einem späteren LTS-Release unterstützen wird (je nachdem, welcher Zeitraum länger ist). Das bedeutet, dass Sie immer mindestens drei Jahre Zeit haben, um Ihre Anwendung auf das nächste LTS-Release zu aktualisieren. Weitere Informationen zur Supportrichtlinie für .NET Core und dazu , was voraussichtlich für die Supportrichtlinie für .NET 5 und höher erwartet werden kann, finden Sie unter bit.ly/2Kfkkw0.

Releasezeitplan für .NET
Abbildung 3: Releasezeitplan für .NET

An diesem Punkt ist .NET 5 noch immer nur eine Ankündigung – eine Absichtserklärung, wenn Sie so wollen. Es gibt noch viel zu tun. Dennoch ist die Ankündigung bemerkenswert. Als .NET Core erstmals veröffentlicht wurde, war es das Ziel, eine plattformübergreifende .NET-Version bereitzustellen, die Azure in den Vordergrund stellen konnte (vielleicht insbesondere die Platform-as-a-Service-Anteile [PaaS] von Azure und die Unterstützung von .NET unter Linux und in Linux-Containern).

Im ursprünglichen Konzept wurde die Idee, .NET Framework vollständig in .NET Core zu portieren, nicht als realistisch angesehen. Etwa zur Zeit der Veröffentlichung von .NET Core 2.0 begann sich das zu ändern. Microsoft erkannte, dass ein Frameworkstandard für alle Versionen von .NET Framework definiert werden musste, damit Code, der unter einem Framework ausgeführt wird, in ein anderes Framework portiert werden kann.

Dieser Standard wurde natürlich als .NET Standard bekannt. Sein Zweck war es, die API zu identifizieren, die ein Framework unterstützen musste, damit Bibliotheken, die den Standard nutzen, darauf zählen konnten, dass eine bestimmte Sammlung von APIs verfügbar war. Wie sich herausstellte, wurde die Definition des Standards und die anschließende Implementierung mit Xamarin/Mono, .NET Core und .NET Framework zu einer Schlüsselkomponente, die die Vereinheitlichungsstrategie von .NET 5 ermöglichte.

Sobald beispielsweise jedes Framework Code implementiert hat, der die API-Sammlung von .NET-Standard unterstützt, erscheint es logisch, darauf hinzuarbeiten, die einzelnen Codebasen zu einer einzigen zu kombinieren (eine Art Refactoring). Und dort, wo das Verhalten nicht identisch ist (z.B. JIT- im Gegensatz zu AOT-Kompilierung): Warum sollte man den Code nicht so zusammenführen, dass alle Plattformen sowohl Ansätze als auch Funktionen unterstützen? Der Aufwand ist nicht trivial, aber das Ergebnis ist ein großer Fortschritt bei der Verringerung von Komplexität und Wartungsaufwand bei gleichzeitiger Vereinheitlichung der Funktionen auf allen Plattformen.

Vielleicht überraschend: Das gleiche .NET Standard, das die Vereinheitlichung ermöglicht hat, wird wahrscheinlich .NET Standard irrelevant machen. In der Tat ist es mit dem Erscheinen von .NET 5 zweifelhaft, ob es eine weitere Version von .NET Standard geben wird – NET 5 und jede Nachfolgeversion wird den Standard darstellen.

Zusammenfassung

Es wird gesagt, dass Timing alles bedeutet, und das gilt auch für .NET 5. Eine nahezu vollständige Umschreibung von .NET Framework war zu Beginn der Entwicklung von .NET Core nicht einmal vorstellbar. Damals reagierte Microsoft auf die Forderung, die Azure-Hostingfunktionen unter Linux, in Containern und für PaaS deutlich zu verbessern. Daher konzentrierte sich das Unternehmen auf die Entwicklung von Lösungen, die den Anforderungen der Kunden und des Azure-Produktteams gerecht wurden.

Mit .NET Core 2.0 wurde die Aufgabenstellung erweitert, um die Funktionalität von .NET Framework zu erreichen. Erneut war das Team darauf fokussiert, etwas Tragfähiges zu veröffentlichen, anstatt sich zu viel vorzunehmen. Aber mit .NET Core 3.0 und der Implementierung von .NET Standard 2.1 begann sich die Situation zu ändern. Die Idee, Änderungen an drei verschiedenen Frameworks vorzunehmen, wenn ein neues Feature oder ein Fehler auftauchte, war eine Irritation und eine Kostenfrage. Und wie bei jedem guten Entwickler entstand bald die Idee, den Code so weit wie möglich in einer einzigen Codebasis umzugestalten.

Und so wurde .NET 5 geboren. Gleichzeitig entstand dabei die Idee, alle Funktionen eines jeden Frameworks zu vereinheitlichen – ob einfache CSPROJ-Formate, die Übernahme von Open-Source-Entwicklungsmodellen, die Interoperabilität mit Java und Objective-C (einschließlich Swift) oder die Unterstützung von JIT- und AOT-Kompilierung. So wurde die Idee eines einzigen, einheitlichen Frameworks zu einem offensichtlichen nächsten Schritt, und ich erwarte, dass er von allen Entwicklern innerhalb und außerhalb von Microsoft begrüßt wird.


Mark Michaelis ist der Gründer von IntelliTect und arbeitet als leitender technischer Architekt und Trainer. Seit fast zwei Jahrzehnten ist er ein Microsoft MVP und seit 2007 Microsoft-Regionalleiter. Michaelis arbeitet in verschiedenen Microsoft-Softwareentwicklungs-Reviewteams mit, einschließlich C#, Microsoft Azure, SharePoint und Visual Studio ALM. Er hält häufig Vorträge bei Entwicklerkonferenzen und hat viele Bücher geschrieben, einschließlich seines letzten „Essential C# 7.0 (6th Edition)“ (itl.tc/­EssentialCSharp). Sie können ihn auf Facebook unter facebook.com/Mark.Michaelis, über seinen Blog unter IntelliTect.com/Mark, auf Twitter: @markmichaelis oder per E-Mail unter mark@IntelliTect.com erreichen.

Unser Dank gilt dem folgenden technischen Experten bei Microsoft für die Durchsicht dieses Artikels: Rich Lander


Diesen Artikel im MSDN Magazine-Forum diskutieren