Auf dem Weg zu C# 7: Sprachfeatures und Entwicklungsprozess

Die nächste Version der Programmiersprache C# entsteht dank Microsofts Hinwendung zur Open-Source-Entwicklung unter den Augen der Öffentlichkeit. In einem ersten Design Meeting hat das zuständige Team die Weichen für das weitere Vorgehen gestellt, auf das Programmierer aktiv Einfluss nehmen können.

In Pocket speichern vorlesen Druckansicht 2 Kommentare lesen
Lesezeit: 13 Min.
Von
  • Robin Sedlaczek
Inhaltsverzeichnis

Die nächste Version der Programmiersprache C# entsteht dank Microsofts Hinwendung zur Open-Source-Entwicklung unter den Augen der Öffentlichkeit. In einem ersten Design Meeting hat das zuständige Team die Weichen für das weitere Vorgehen gestellt, auf das Programmierer aktiv Einfluss nehmen können.

Dank der neuen Offenheit von Microsoft lassen sich die Teams des Softwareanbieters nun bei ihrer Arbeit über die Schulter schauen. Doch nicht nur das, die Community ist aufgerufen, eigene Vorschläge in Form von Pull Requests in den GitHub-Repositories des Unternehmens einzureichen. Die Offenlegung des Codes und ein neuer Entwicklungsprozess mit starkem Fokus auf die Kollaboration mit der Community gewährt dabei frühzeitige Einblicke in die Arbeit von Microsoft und die Funktionen, die in seinen Techniken unterkommen sollen.

Das Projekt "Roslyn", formell als .NET Compiler Platform bezeichnet, hat vor dem Hintergrund eine hohe Bedeutung. Mit ihm legt der Softwareanbieter eine der wichtigsten Komponenten seiner Entwicklungsplattform offen: die Compiler des .NET Framework, darunter die Übersetzer für C# und VB.NET.

Der Quellcode ist dabei aber nicht das Einzige, was Microsoft offenlegt. Diskussionen und Design Meetings gehören ebenso zum offenen Entwicklungsprozess mit der Community. Am 21. Januar 2015 veröffentlichte das Team um Projekt Roslyn das Protokoll aus dem ersten Design Meeting für C# 7.

Da die Entwicklung von Open-Source-Projekten Neuland für Microsoft ist, muss zuerst ein Prozess etabliert werden, der die Weiterentwicklung des .NET Framework und der zugehören Sprachen mit zunehmender Offenheit und unter Einbezug der Community gewährleistet. Trotz scheinbar unerschöpflicher Ideen aus der Gemeinschaft muss Microsoft aber weiterhin einen gewissen Release-Zyklus und damit eine Planbarkeit sicherstellen, was nicht zuletzt für Geschäftskunden einen hohen Wert hat.

Aus dem Grund führt ein Kernteam von Microsoft die Design Meetings durch. Das Einbeziehen externer Mitarbeiter ist geplant. Die Zusammentreffen sollen via Aufzeichnung oder Live-Streams veröffentlicht werden. Protokolle werden letztlich als Issues auf GitHub zur Verfügung gestellt, um über die Plattform wiederum Feedback aus der Community sammeln zu können. Entwickler können ihre Ideen dort ebenfalls als Issues einbringen.

Um Interesse an einzelnen Ideen zu demonstrieren, lässt sich die User Voice verwenden. Ist die Nachfrage hoch genug, nimmt das Design Team die Idee in die Agenda eines Meetings auf. Eines der Teammitglieder bekommt dann die Verantwortung für den Vorschlag, sammelt Feedback, kümmert sich um das Entwurfsdokument (Proposal Document) und sorgt für entsprechenden Fortschritt zwischen den Treffen. Feature-Ideen werden mit Prototypen getestet.

Interne sowie externe Pull Requests können beim Entwurf eines neuen Features und bei der Entscheidung helfen, es in den Sprachumfang aufzunehmen. Für die finale Entscheidung behält Microsoft sich allerdings das Stimmrecht vor und somit letztlich die Oberhand. Aus dem Grund beschreibt der Softwarehersteller den Prozess nicht als demokratisch, sondern als eine "wohltätige Diktatur".

In dem im Januar durchgeführten ersten Design Meeting wurde aber nicht nur der Entwicklungsprozess diskutiert. Es ging auch um die Features kommender Sprachversionen. Dabei ist es wichtig zu beachten, dass nicht beliebige Sprachelemente bearbeitet werden können. Oftmals existieren Abhängigkeiten unter ihnen, weshalb man sie in einem Gesamtkontext betrachten muss.

Aus dem Grund postuliert das Design-Team unterschiedliche Themenbereiche, in denen es die Sprachen weiterentwickelt – nicht zuletzt, um die Arbeit zu strukturieren. An der Stelle sei erwähnt, dass das Design-Team die Sprachen C# und VB.NET nicht getrennt betrachtet. Vielmehr sollen alle Überlegungen und Features für beide Sprachen Gültigkeit haben und in beide Einzug halten.

Folgende Themenbereiche hat das Team isoliert:

Objektorientierte Sprachen sind verhaltensorientiert. Betrachtet man moderne Anwendungen, hat sich der Umgang mit Daten relativ stark verändert. Längst werden Daten nicht mehr nur aus großen Datenbanken gelesen, von einem Benutzer bearbeitet und wieder gespeichert. Vielmehr ist das verteilte Produzieren und Konsumieren kleinerer Datenhäppchen Thema – in Services, IoT-Anwendungen, mobilen Apps, Desktop-Anwendungen und so weiter. Daraus ergeben sich neue Anforderungen an den Umgang mit Daten. Inspiriert von funktionalen Sprachen, möchte man etwa Konzepte wie Pattern Matching, Unveränderbarkeit, Slicing und Tupel in die Sprache aufnehmen.

Der engen Beziehung zwischen C# und .NET geschuldet, adressiert das Design-Team Performance- und Zuverlässigkeitsprobleme der Sprachplattform. So erfordern zum Beispiel Strukturtypen und generische Typen oftmals Kopiervorgänge und lassen sich nicht per Referenz verarbeiten. Das verursacht Performance-Einbußen, insbesondere auf kleineren Geräten.

Die Zuverlässigkeit der Sprachen hingegen spiegelt sich etwa im Speichermanagement wider. C# und VB.NET sind verwaltete Sprachen und gehen weitgehend sicher mit dem Speicher um. Probleme treten auf, wenn man sich das Löschen und Finalisieren von Objekten anschaut – insbesondere an der Grenze zu nicht verwaltetem Code. Microsoft Research beschäftigt sich intensiv mit derartigen Themen. Ergebnisse aus der Forschungsarbeit sollen in beide Sprachen einfließen.

Microsoft veröffentlichte jüngst den Quellcode der CoreCLR. Dabei handelt es sich um eine abgespeckte .NET-Laufzeitumgebung, speziell für den Einsatz in der Cloud optimiert. Nicht nur daran lässt sich ablesen, dass das .NET Framework flexibler wird. Auch die Anzahl der auf GitHub gehosteten Repositories spricht eine deutliche Sprache. Aus derartigen Entwicklungen ergeben sich neue Anforderung für die Modularisierung von Code und die Komposition von Softwareeinheiten. Generalisierte Erweiterungsmethoden sind dabei eines der wenigen Sprachelemente, mit denen Microsoft dem begegnen möchte. Vielmehr hält das Unternehmen in dem Zusammenhang Arbeit am Framework und Tooling für nötig. Statisches Linken statt IL Merge, Determinismus, Versionierung und Unterstützung für NuGet sind nur ein paar Themen, die dabei eine Rolle spielen.

Die verteilte Natur des modernen Rechnens soll ebenfalls bei den Neuerungen in C# Beachtung finden. Präziser geht es um die Verteilung und parallele Ausführung datenverarbeitender Prozesse sowie Berechnungen im weitesten Sinne. Parallelität und Asynchronität sind hier die Schlagworte. Entsprechende Konzepte müssen sich in den Sprachen wiederfinden, um den Umgang mit der Parallelität zu vereinfachen und verteiltes Rechnen überhaupt erst zu ermöglichen. Dazu wurden mit C# 5 beispielsweise async[i] und [i]await eingeführt. Die Schlüsselworte vereinfachen den Umgang mit nebenläufigen Prozessen, indem der Compiler entsprechenden Code zum Erzeugen und Synchronisieren von Threads generiert.

Bisher bietet das async/await-Feature lediglich die sogenannte Single-Value Asynchrony. Das bedeutet, dass sich nur genau ein Wert zurückgeben lässt. Das manifestiert sich im Typ Task<T>, den asynchrone Methoden an den Aufrufer liefern müssen. Um das verteilte Rechnen besser zu unterstützen, sollen kommende Versionen der .NET-Sprachen asynchrone Sequenzen und Streams beherrschen. Ein weiteres Thema ist die Serialisierung von Daten: In Zukunft soll es keine direkt eingebaute Serialisierung mehr geben. Stattdessen wird der Fokus auf die Unterstützung für benutzerdefinierte Serialisierung gerichtet, wobei Performance unter Vermeidung von Reflexion im Vordergrund steht.

Metaprogrammierung bedeutet vereinfacht gesagt, dass Programme andere Programme schreiben. Code generiert also Code. Die Idee dazu kommt aus dem Bedürfnis nach adaptiven Programmen, die sich an ihre jeweilige Umgebung anpassen können. Microsoft plant in dem Bereich keine umfassende Unterstützung statischer Metaprogrammierung (also solche, die zur Kompilierzeit stattfindet) in den .NET-Sprachen. Als Grund führt das Design-Team Bedenken bezüglich der Performance im Tooling auf, da Änderungen an einer Code-Datei Änderungen an anderen verursachen könnten. Mit dem Anspruch, innerhalb von 20 Millisekunden auf Tastendrücke in Visual Studio zu reagieren, wäre das nicht vereinbar.

Das Design-Team möchte den Fokus daher auf Sprachelemente richten, die das Softwaredesign vereinfachen und das Bedürfnis nach Metaprogrammierung verringern. Sprachfeatures, mit denen sich das umsetzen lässt, nennt das Team ebenfalls. Darunter fallen zum Beispiel virtuelle Erweiterungsmethoden, generische Einschränkungen für Konstruktoren, Einschränkungen für Aufzählungen und Delegaten, Mixins und Traits. Unter Letzteren versteht man zusammenhängende, wiederverwendbare Funktionsbündel, die sich in Klassen einhängen lassen. Diese Konzepte der objektorientierten Programmierung sind beispielsweise aus den Sprachen Ruby und Scala bekannt.

Um C#-Programme weniger fehleranfällig zu gestalten, treiben die Entwickler die Unterstützung von Null-toleranten Operationen voran. So gibt es ab C# 6 den Null-Bedingungsoperator (?.). Mit ihm werden Tests auf Null-Werte für Referenzen erleichtert. Zum Beispiel bewirkt der Ausdruck myObject?.DoSomething(), dass die Methode DoSomething nur aufgerufen wird, wenn die Referenz in myObject ungleich Null ist. Für C# 7 werden nun Konzepte evaluiert, die an der Stelle ein Stück weiter gehen sollen.

Ein großes Thema dabei sind Referenzen, die als non-nullable bezeichnet werden. Das bedeutet, dass eine Variable eines Referenztypen zur gesamten Laufzeit des Programms nicht Null werden kann. Sie einzusetzen würde Tests auf Null im Code ersparen und könnte viele Fehler im Vorfeld ausschließen. Leider ist Microsoft selbst ein schwerwiegender Fehler unterlaufen: Deratige Referenzen waren zu Beginn der Entwicklung von .NET nicht im Design vorgesehen. Das ist beim Entwurf eines Typsystems aber notwendig, da das Hinzufügen des Konstruktes im Nachhinein zu Inkompatibilitäten in existierenden Anwendungen führt. Zusätzlich sind Referenzen, die nicht Null werden, technisch recht schwierig umzusetzen. Was passiert zum Beispiel, wenn man auf ein solches Feld im Konstruktor zugegreift? Die Memory Allocator Common Language Runtime initialisiert alle Referenzfelder einer Klasse mit Null, bevor das Programm den Konstruktor aufruft. Ein weiteres zu lösendes Problem ist das Konvertieren von Referenzen, die Null zulassen, in solche, die non-nullable ist. In was würde konvertiert, wenn die Referenz Null wäre? Wird die Zielvariable kanonisch initialisiert oder soll die CLR eine Exception werfen? Im Laufe der kommenden Design Meetings zu C# 7 wird man sehen, wie Microsoft diese Themen adressiert.

Neben der Darstellung der Themenkomplexe ging das Design-Team auf fundamentale Sprachmerkmale ein, die zur Betrachtung und Umsetzung der oben genannten Features grundlegend sind. Für den Umgang mit Typen ist beispielsweise ein umfassendes Pattern Matching Framework vorgesehen. Beim Pattern Matching (= Musterabgleich) geht es um das Suchen diskreter Strukturen oder Teilstrukturen auf Grundlage von Symbolen. Ein prominentes Beispiel ist wohl die Wildcard-Suche in Zeichenketten. Im Kontext von C# geht es allerdings um Daten und Datentypen: Man möchte herausfinden, ob Daten eine bestimmte Form aufweisen, und sie im Anschluss extrahieren. Das Augenmerk dabei liegt auf dem is-Operator und der switch-Anweisung. Ergebnis beider Befehle ist ein Boolescher Wert, der angibt, ob das Muster passt oder nicht. Wenn ja, dann sollen die im Muster verwendeten Variablen im gesamten Scope der Anweisung Gültigkeit besitzen. Die folgenden Beispiele verdeutlichen dies.

Zuerst einmal für den [i]is[]/i]-Operator, der auf folgende Art verwendet werden könnte:

if (o is Point(*, 5) p) Console.WriteLine(o.x); 
if (o is Point p) Console.WriteLine(p.x);
if (p is (var x, 5) ...

Für die switch-Anweisung könnte das Pattern Matching wie folgt aussehen:

switch (o) {
case string s:
Console.WriteLine(s);
break;
case int i:
Console.WriteLine($"Number {i}");
break;
case Point(int x, int y):
Console.WriteLine("({x},{y})");
break;
case null:
Console.WriteLine("<null>);
break
}

Ein weiteres interessantes Sprachmerkmal sind die Methodenverträge. Die aus dem Microsoft-Forschungsprojekt Spec# hervorgegangenen Annotationen für Vor- und Nachbedingungen für Methoden sind sehr umfangreich und bereits voll in das .NET Framework integriert. Allerdings erzeugt die Art der Umsetzung mittels Attributen relativ unschönen Code. Daher wäre eine Integration direkt in die Sprachsyntax wünschenswert. Das würde wiederum die volle Integration in die Laufzeitumgebung ermöglichen, wo der volle Funktionsumfang zur Geltung käme. Führt man sich weiterhin den Code vor Augen, den man zum Prüfen von Werten von Parametern schreibt, so reduzieren in Sprachen integrierte Methodenverträgen derartigen Code. Methodenimplementierungen werden sauberer und besser lesbar. Ein weiterer Vorteil, der mit dieser Spracherweiterung einhergeht, ist die Möglichkeit, Metadaten zu generieren. Letztere lassen sich dann im Tooling der Sprache anzeigen und verwenden. Das folgende Beispiel zeigt, wie Methodenverträge aussehen können:

public void Remove(string item)
requires item != null
ensures Count >= 0
{
...
}

Im ersten Design Meeting zu C# 7 hat das Microsoft-Team den offenen Entwicklungsprozess wieder ein Stück vorangetrieben, um die Community weiter in die Entwicklung der Sprachplattform zu integrieren. Neben Grundlagen zum weiteren Vorgehen zeigte das Team technische Themenbereiche auf, anhand derer die Entwicklung stattfinden soll. Der Umgang mit Daten, die Performance, die Robustheit von Programmen und die Codequalität haben dabei eine hohe Bedeutung. Es bleibt abzuwarten, was das Team aus seinen Plänen macht und inwieweit die Community tatsächlich Einfluss auf die Sprachplattform nehmen wird.

Robin Sedlaczek
hat 15 Jahre Erfahrung als professioneller Softwareentwickler, leitete einige Softwareprojekte und arbeitet seit 4 Jahren als CTO bei der Fairmas GmbH. In seiner Freizeit vermittelt er sein Wissen in Fachvorträgen, Onlinekursen, Fachartikeln und in seinem Blog.
(jul)