Mit der Veröffentlichung von .NET 5 gibt es auch eine neue Version von C#. Welche Neuerungen gibt es in alltäglichen Codingsszenarien, beim Bestreben nach größerer Klarheit und Einfachheit mit den Sprachfeatures von C# 9? Einige dieser Änderungen werden nachfolgend mit kleinen Codebeispielen gezeigt, um einen ersten Eindruck bekommen zu können, was sich geändert hat und was neu hinzugekommen ist.
Init-Only Properties
Objekt-Initialisierer sind sehr nützlich, denn durch diese bekommt man einen flexiblen, übersichtlichen Weg, um Objekte zu erstellen. Doch hinter den Kulissen ruft ein Objekt-Initialisierer den Konstruktor auf und setzt im Nachgang die Properties. Das funktioniert bei Immutable Klassen nicht, denn dort sollen sich Properties im Nachgang nicht mehr ändern, sondern nur initial im Konstruktor gesetzt werden.
Durch Init-Only Properties wird dieses Problem behoben. Werden Properties statt mit einem set mit einem init versehen, so können Properties im Konstruktor oder im Objekt-Initialisierer gesetzt werden. Danach bleibt die Klasse immutable und Properties können nicht mehr geändert werden.
public class Car { public string Manufacturer { get; init; } public string Model { get; init; } public string FuelType { get; init; } } var car = new Car { Manufacturer = "Manufacturer 1", Model = "Model 1", FuelType = "Gasoline" }; // Ok var car2 = new Car(); car.Manufacturer = "Test"; // Error
Init Accessors und Readonly Felder
Da Init Accessors nur während der Objekt-Initialisierung verwendet werden können, ist es auch möglich, dass sie Readonly Felder verändern können, wie dies auch ein Konstruktor heute kann:
public class Car { private readonly string manufacturer = "<anyManufacturer>"; private readonly string model = "<anyModel>"; public string Manufacturer { get => manufacturer; init => manufacturer = (value ?? throw new ArgumentNullException(nameof(manufacturer))); } public string Model { get => model; init => model = (value ?? throw new ArgumentNullException(nameof(model))); } }
Record Types
Mit der Veröffentlichung von C# 9 gibt es nun Record Types. Diese neue Art, Klassen zu definieren, ist besonders für Datenstrukturen geeignet. Denn bei Record Types spart sich der Entwickler das Schreiben von Boiletplate Code, wie z.B. einem Konstruktur, die Überladung von Equality Methoden wie Equals, == und auch GetHashCode und selbst eine ToString-Methode wird bereitgestellt, die die aktuellen Werte in einem JSON-ähnlichen Format ausgibt.
public record Car { public string Manufacturer { get; init; } public string Model { get; init; } public string FuelType { get; init; } } var car = new Car { Manufacturer = "Manufacturer 1", Model = "Model1", FuelType = "Diesel" }
Zusätzlich zum Record Type gibt es auch noch Positional Records. Diese sind noch leichtgewichtiger und benötigen nur die Properties einer Klasse und alles weitere wird im Hintergrund generiert:
public record Person(string FirstName, string LastName); var tim = new Person("Tim", "Steiner");
With-Expressions
Wenn man mit Immutable Daten arbeitet, kommt früher oder später der Zeitpunkt, an dem man eine Kopie des Objekts mit veränderten Werten benötigt. Bei unserem Auto ist das z.B. der Fall, wenn ein neues Auto vom selben Hersteller und Modell mit einem anderen Kraftstofftyp ausgestattet werden soll. Dafür kann das with-Schlüsselwort verwenden werden. Dies funktioniert aber nur, wenn ein Record verwendet wird.
var car = new Car { Manufacturer = "Manufacturer 1", Model = "Model 1", FuelType = "Gasoline" }; var eCar = car with { FuelType = "Electricity" }; // much cleaner fuel now!
Die with-Expression kopiert den kompletten Zustand des alten Objekts in ein neues Objekt und verändert die dort angegebene Eigenschaft. Dafür muss das veränderte Property allerdings einen init oder set Property-Initialisierer besitzen.
Zusätzlich gilt: Die Kopie des Objekts ist nur eine flache Kopie. Das bedeutet das Properties mit Referenzen auf andere Objekte einfach übernommen werden. Hier werden nicht zusätzlich noch referenzierte Objekte geklont.
Top-Level Programs
Wenn man in C# ein einfaches Programm schreiben möchte, benötigt man auch etwas Boilerplate Code:
using System; namespace Demo { public static class TopLevelPrograms { public static void Main() { Console.WriteLine("Hello World"); } } }
Dies geht nun viel einfacher, denn all der Boileplate Code kann einfach weggelassen werden:
System.Console.WriteLine("Hello World");
Dies funktioniert genau für eine Datei im Projekt. Entdeckt der Compiler mehrere Dateien mit Top-Level Statements, so wirft dieser einen Fehler. Auf Argumente kann direkt über die “globale Variable” args zugegriffen werden und auch asynchrone Anweisungen die mit await erwartet werden, wandelt der Compiler richtig um.
Besonders nützlich ist dies, wenn kleine Prototypen erstellt werden, für die man nur ein paar Zeilen Code testen möchte. Dies ermöglicht es C# Code wie in Scriptsprache zu schreiben, ähnlich wie viele das von Jupyter Notebooks kennen.
Pattern matching enhancements
Das Pattern Matching in C# wurde durch verschiedene neue Patterns erweitert, was diverse Codezeilen noch lesbarer macht.
public static bool IsLetter(this char? c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
Zusätzlich können jetzt Ausdrücke mit dem not Keyword einfach negiert werden:
if (obj is not null) { // do something }
Fazit
Mit C# 9 kommen viele neue Sprachfeatures, die vieles einfacher machen und gewisse Entwicklerprobleme adressieren. Ein Blick in die kompletten Features von C# 9 lohnt sich daher auf jeden Fall, denn es gibt etliche weitere Features wie z.B. target-typed new expressions, Covariant returns und vieles mehr.
Der aktuelle Feature Status kann auf GitHub nachgelesen werden und auch die Microsoft Dokumentation gibt Aufschluss über die neuen Features. Ich persönlich empfinde die Neuerungen als sehr gelungen und bin gespannt wie ich diese in meinen Entwickleralltag integrieren kann.
Nachdem .NET 5 und die aktuellste Version von Visual Studio 2019 installiert wurde, kann es auch schon los gehen.
Sprechen Sie uns an, wenn auch Sie Fragen zu Themen rund um .NET haben oder Hilfe bei Portierung Ihrer Anwendung nach .NET 5.0 in Ihrem Unternehmen benötigen. Wir führen Sie gerne ans Ziel.