.NET versus .NET

Autor
Rolf Wenger
Thema
.NET Framework Kompatibilität zu .NET Core
Lesezeit
6-8 Minuten

Abstrakt

Die Programmierung von Anwendungen geschieht immer mit der Verwendung eines Application Programming Interfaces (API). Das verwendete API bestimmt dabei die Möglichkeiten der Anwendung und die Möglichkeiten auf welchen Plattform diese später lauffähig ist. Mit dem .NET Framework 4.8 und dem .NET 10 sind heute zwei, gemäss Microsoft kompatible Frameworks 2er Generationen am Start. Oberflächlich betrachtet besteht der Unterschied der beiden API nur darin, dass .NET 10 Anwendungen auf Windows, LINUX und OSX lauffähig sind, hingegen .NET Framework Anwendungen nur auf Windows laufen.

Dieser Artikel beleuchtet die Kompatibilität der beiden Frameworks und zeigt ein paar perfide Unterschiede auf, die Sie bei Modernisierungen und Neuentwicklungen berücksichtigen müssen.

Einführung

Vor mehr als 25 Jahren wurde das .NET Framework von Microsoft eingeführt. Damals war eine der Begründungen, dass das Windows API WIN32 überaltert ist, und durch eine neues, modernen Ansprüchen genügendes, API ersetzt wird. Im Weiteren wurde angekündigt, dass alle 10 bis 15 Jahre mit weiteren Erneuerungen zu rechnen ist.

Ende 2025 ist das WIN32 API immer noch DIE Grundlage von Windows auf dem auch das .NET Framework aufbaut. Allerdings ist bereits die Version 10 des Nachfolgeframeworks .NET verfügbar. Die Namensverwirrung um die verschiedenen .NET Versionen ist seit langem ein Thema und hier noch einmal kurz klargestellt. “.NET Framework” ist der Name für das ursprüngliche Framework, das von den Versionen 1.0 bis über mehr als 25 Jahre bis heute zu der Version 4.8.x entwickelt wurde. Dieses Framework ist mit seinen offiziellen Version nur auf dem Windows Betriebssystem verfügbar und bildet sozusagen die Generation 1 von .NET. Mit dem Namen “.NET Core” führte Microsoft 2016 die Generation 2 des Frameworks ein. Ab 2020 wurde die neue Generation dann weiter umbenannt in .NET X, wobei X für die aktuelle Version steht. Damit die Versionen zwischen den Frameworks nicht weitere Unklarheiten schafften, hat man damals von .NET Core 3 direkt in die Version 5 gewechselt. Damit bleibt die Version 4 reserviert für das ältere .NET Framework. Die 2. Generation von .NET ist eine “kompatible Gesamtüberarbeitung” der Vorgängerversion und ist nicht nur für Windows sondern auch für Linux und OSX verfügbar.

In diesem Artikel werden die Begriffe .NET Framework für die 1. Generation und der Begriff .NET 10 zur Kennzeichnung der neuen Generation verwendet. .NET 10 ist die Version, die Microsoft im November 2025 veröffentlicht hat.

Die Herausforderungen

Aufgrund der Grösse des Marktes von Windows gibt es viele Anwendungen, die mit frühen Versionen von .NET entwickelt wurden. Viele dieser Anwendungen werden Schritt für Schritt auf die neue .NET Generation umgestellt. Es werden aber auch neue Anwendungen auf der grünen Wiese entwickelt, wobei die entsprechenden Entwickler oft nur das Wissen mit dem .NET Framework mitbringen. Reicht das aus? Die untenstehende Tabelle zeigt zunächst eine grobe Übersicht über die Kompatibilitäten der beiden .NET Frameworks und ihren Bestandteilen. Anschliessend werden ein paar konkrete Punkte der Kompatibilität zwischen den Frameworks diskutiert.

Teilgebiet der .NET Umgebung

Metadaten

Kompatibilität

Beginnen wir mit einem wirklich kompatiblen Teil. Bis jetzt verwenden beide Generationen die gleichen Metadaten (gleiche Version der Beschreibung) für Assemblys und Typ-Beschreibungen. Das resultiert darin, dass beide Versionen gegenseitig alle Assemblies “verstehen” ja sogar laden könnten, und eben Kompatibel sind!


Typ-System

Auch hier ist eine komplette Kompatibilität für das Typsystem und der Abbildung der grundlegenden Typen vorhanden. Allerdings muss man bereits hier wissen, dass sich die Kompatibilität in diesem Fall auf das Compile und Runtime verhalten der Typen selber bezieht. Die Unterbringung der Typen selber in den Assembys hat geändert (Modularisierung) und das kann sich bereits negativ auswirken.


Common Language Runtime

Die CLR ist weitgehend kompatibel. Das gilt aber nicht für die Organisation des Prozessraums, denn .NET unterstützt faktisch keine Application Domains mehr. Das Ladeverhalten von Typen und Assemblies ist oberflächlich kompatibel, aber im Detail unterscheidet es sich deutlich.


Base Class Library

Die BCL ist weitgehend kompatibel. Erschwerend wirkt aber, dass diese weitgehende Kompatibilität eben nicht 100 prozentig ist. Das heisst es können neue Klassen oder Methoden auf bestehenden Klassen sein, die man nicht kennt, deren Gebrauch aber wichtig wären, oder umgekehrt oft verwendete Methoden, die einfach nicht mehr vorhanden sind.


Reflexion

Hier gibt es entscheidende Game-Changer. Wer Reflexion braucht muss aufpassen, dass er die Änderungen mitbekommt (siehe auch Details unten).


Windows Management Interface

Der Name dieses Teils von .NET birgt hier schon eine Aussage zur Kompatibilität. Es gibt kein Linux Management Interface und somit ist das WMI nur in .NET 10 verfügbar, wenn die Register für Windows eingeschaltet werden. Das hat aber zur Folge, dass eben dann die Anwendung nicht auf Linux oder OSX lauffähig ist.


WPF

In .NET 10 verfügbar, aber mit der Einschränkung, dass die Anwendung nur auf Windows verwendet werden kann. Das ganze kann auch einen Einfluss haben auf die Projektdefinitionen und Verfügbarkeit von verwendeten Bibliotheken (Nugets).


WCF

Standardmässig nicht mehr verfügbar in .NET 10. Es gibt allerdings ein verfügbares Open Source Projekt, das in einem Projekt mit einbezogen werden kann.


Web-Entwicklung

Ein separates, abendfüllendes Thema. Hier ist die Kompatibilität maximal abweichend. Ich denke aber, dass hier eher die Geschwindigkeit der technologischen Entwicklung den Ausschlag gibt, und einfach Technologiesprünge zusammen mit der Einführung von .NET 5 gemacht wurden.


Im Weiteren sind folgende Spezialitäten (Auflistung nicht vollständig) entweder gar nicht oder nur auf .NET 10 für Windows verfügbar, oder deren Verhalten ist pro OS unterschiedlich:

  • Windos Performance Counter
  • P-Invoke
  • Verschlüsselung
  • Filezugriffe (Gross- / Kleinschreibung)
  • Dateistrukturen (Program Files, Program Data, Document-Folder …)
  • Enviroment
  • Code Access Security (komplett abgeschafft)

Konkrete Punkte

Im Rahmen unserer Erfahrungen stelle ich hier die Punkte ins Zentrum, die bei der Entwicklung und Umstellung von .NET Framework auf .NET 8.0 am meisten Zeit geraubt haben, oder die grössten Kompatibilitätsirrtümer entstanden sind:

Organisation des Prozessraums und Konfiguration

Microsoft kommuniziert klar und deutlich, dass in .NET 10 gegenüber dem älteren Framework “nur noch” die standard Application Domain unterstützt wird. Alles kein Problem denkt man sich, mit dem kann ich leben. Aber bei genauerem Hinsehen entpuppt sich das ganze als Kompatibilitäts-Mogelpackung, denn die entsprechende Klasse AppDomain ist nur ein Köder, das man meint es funktioniert alles gleich. Spätestens bei der Ecke in bestehendem Code, bei dem man eigene AppDomains nutzt, wird es dann schnell klar, dass alles anders funktioniert. Achtung: Anders heisst hier nicht schlechter – ich bin der Meinung sogar viel durchdachter, aber man muss den Einfluss zuerst verstehen.

Neben der veränderten Runtime-Umgebung muss auch berücksichtigt werden, dass das Starten und die Konfiguration von Assemblys komplett überarbeitet wurde und die Konfiguration von Anwendungen oder die Erstellung von Executables sich sehr stark von der ersten Generation von .NET unterscheidet. Die Unterschiede hier sind zwei wesentlichen Einflüssen geschuldet: der Unterstützung von andern OS und der vermehrten Verwendung von Datenformaten wie JSON oder YAML. So gesehen wird dann schnell klar, dass eben der Start einer .NET Anwendung nicht mehr nahtlos im Betriebssystem eingebunden werden kann, wie das beim .NET Framework unter Windows war, sondern spezielle Loader generiert werden (die Executables), die dann den eigentlichen eigenen Code aus den Bibliotheken laden.

Typsystem und Assembly-Strukturen

Das Typsystem von .NET hat gegenüber den älteren APIs aus dem letzten Jahrhundert phänomenale Erleichterungen für die Entwicklung gebracht. Diese werden mit .NET 10 fortgesetzt und die Kompatibilität ist perfekt – bis zu dem Moment, bei dem man eigene Mechanismen für die Serialisierung vorsieht. Im Umgang mit den Typen muss man unbedingt die neuen Assembly-Strukturen von .NET 10 berücksichtigen. Da ist nämlich kein Stein auf dem andern geblieben, denn alle bestehenden grundlegenden Klassen und auch die Klassen der BCL sind in andern Assemblys untergebracht. Das wiederum hat einen enormen Einfluss auf andere Mechanismen, denn ich erinnere Sie daran: ein Typ Name besteht in .NET aus “<Namensraum>.<Typname>,<AssemblyName>”. Somit kann ein gespeicherte Datei mit Typ Namen nur dann erfolgreich wieder geladen werden, wenn die Basistypen in den richtigen Assemblys gesucht werden.

Reflexion

Wo beginnt Reflexion und wo endet sie? Die Antwort darauf ist nicht ganz einfach, den es gibt fliessende Grenzen. Sobald Sie aber mit den Metadaten von Typen, Methoden und so weiter arbeiten, dann sind Sie definitiv im Gebiet der Reflexion angekommen. Dazu gehört aber auch die Umwandlung von Typinformation in Textform zu effektiven Typen. Die wohl einfachste Art so etwas zu machen besteht darin, die Methode Type.GetType(“Namesraum.Typname, Assembly Name”) zu verwenden. Im älteren .NET Framework wurden mit dieser Methode auch die angesprochenen Assemblys automatisch geladen und dann der gesuchte Typ zurück gegeben. in der modernen Version von .NET 10, wird der Typ nur zurückgegeben, wenn die Assembly, in der der Typ deklariert ist, schon im Speicher ist. Ist dies nicht der Fall, liefert die Methode null. Das ist ein Runtime-Kompatibilitätsproblem, das uns einiges an Kopfzerbrechen gekostet hat.

Mit dem soeben definierten Verhalten kommt man dann leicht zu der Frage, wie lade ich explizit eine Assembly. Die Antwort zu dieser Frage überrascht, weil sie nach verwendetem Framework unterschiedlich ausfällt (siehe untenstehende Tabelle). Man beachte, dass dass das Laden der Assembly auch in Bezug auf den Kontext noch beurteilt werden muss. Das löst dan die nächste Untersuchung der Inkompatibilität resultiert, weil spätestens jetzt sind Sie dann in der AppDomain-Kompatibilitätshölle gelandet.

Framework - Laden der Assembly

.NET Framework
Assembly.LoadFrom("Assembly-Name")
.NET 10
AssemblyLoadContext.Default.LoadFromAssemblyPath("Assembly-Name");

Base Class Library

Die BCL als Ganzes weist eine relativ hohe Kompatibilität zwischen den unterschiedlichen Welten auf. Allerdings muss man sich bewusst sein, dass auch triviale und oft verwendete Klassen mit neuen Funktionen ergänzt wurden, oder bestehende Methoden nicht mehr unterstützt werden. Es macht also auch hier in jedem Fall Sinn, bestehendes Wissen zu aktualisieren, indem man bei der ersten Anwendung kurz einen Blick auf die online Hilfe wirft und studiert was da neu ist.

Als Beispiel verwende ich zwei Dinge: .NET 10 definiert einige nette neue Funktionen für die Behandlung von Strings, die einfach nicht in das .NET Framework übernommen werden. Das verwirrt spätestens dann, wenn Sie Code schreiben, der auf beiden Plattformen lauffähig ist. Da kann es Passieren, dass Visual Studio beim Editieren mit Nutzung der IntelliSense und damit generiertem Code reklamiert und definiert, dass eine Methode nicht überall verfügbar ist, obschon der Code von Visual Studio so generiert wurde. Als zweites noch konkreteres Beispiel verwende ich eigene Ausnahmeklassen. Hier hat man früher die speziellen Serialisierungs-Methoden in Form eines speziellen Konstruktors für die Deserialisierung und einer speziellen Serialisierung geschrieben. Diese beiden Methoden existieren in .NET 10 schlicht nicht mehr.

Empfehlungen

Dem Einsatz von .NET 10 ist sicher nichts mehr entgegen zu setzen. Im Gegenteil, wer aufhört sich mit den neusten Versionen zu beschäftigen und die Vorteile zu suchen, der sollte sich überlegen, ob er noch den richtigen Job macht.

Auf der andern Seite, empfehle ich heute folgende Fragen zu stellen:

  • Kennen meine Entwickler die Unterschiede zwischen dern beiden Generationen von .NET und wissen Sie was auf sie zukommt, wenn Code umgestellt werden soll?
  • Verwenden wir ausschliesslich die BCL? Wenn ja, ist die Umstellung einfach. Kommen andere Technologie APIs zum Einsatz (WPF, WCF, WMI …) muss ich mir vor einer Vorschnellen Umstellung im Klaren werden was das heisst.
  • Soll der Code später nur auf Windows laufen, ist die Kompatibilität besser. Man pflegt die entsprechenden Referenzen ein schon Kompiliert Code mit Nutzung für WMI und Co. Soll der Code aber auch auf Linux lauffähig sein, empfehle ich eine tiefer gehende Analyse, was es bedeuted den Code umzustellen.
  • Stellen Sie sich die Frage, ob single Code (eine Codebasis) für beide Generationen gepflegt werden soll, oder ob Sie ein bestehendes Produkt als solches pflegen, und für eine .NET 10 Basis einen separaten Code-Branch machen und dann effektiv zwei Sourcen pflegen. Dieser Entscheid ist mit Bedacht zu fällen, denn er hat grosse Konsequenzen, in Bezug auf Codepflege udn Fehlerkorrektur!
  • Entschieden Sie sich für eine Single-Code Basis, sind die Möglichkeiten der Generationen-spzifischen Code-Unterscheidung zu prüfen und auszubilden.

Fazit

Ich habe einige Ausbildungen gemacht und wurde sowohl von Microsoft als auch von andern Personen geblendet, dass .NET 10 eine hohe Kompatibilität aufweist und Code-Migrationen einfach gemacht werden können. Heute bin ich ernüchtert und empfehle eine etwas andere Ausdrucksweise: .NET 10 ist eine Weiterentwicklung des .NET Frameworks mit einer gewissen Kompatibilität. Für eine neue Anwendung gut genug um sich relativ rasch zurecht zu finden. Für die Migration von bestehenden Anwendungen empfehle ich unbedingt ein Migrationskonzept zu schreiben und darin eine Strategie für die Umstellung für sich oder die Anwendung zu definieren.