C# 7 – Stand der Dinge und Ausblick

Ein Blick auf die möglichen Neuerungen für C# 7 zeigt, wie sich die Sprache Anregungen jenseits des Tellerands der objektorientierten Programmierung holt.

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

Ein Blick auf die möglichen Neuerungen für C# 7 zeigt, wie sich die Sprache Anregungen jenseits des Tellerands der objektorientierten Programmierung holt.

Microsoft hat nicht nur den Quellcode der .NET-Compiler-Plattform mit den Compilern für C# und VB.NET offengelegt, vielmehr ist der Softwarehersteller auch transparent hinsichtlich des Entwicklungsprozesses. So lassen sich die Fortschritte des Teams rund um die Compiler-Plattform auf GitHub beobachten. heise Developer berichtete bereits im März 2015 über die neuen Sprachfeatures, die das Roslyn-Team für die kommende Generation seiner .NET-Sprachen diskutiert.

Wie erwartet hat Microsoft auf der diesjährigen Connect();-Konferenz den ersten Release Candidate von .NET Core freigegeben. Inzwischen hat Microsoft die in Abbildung 1 gezeigte Roadmap auf GitHub angepasst. Der Release Candidate hat eine Go-Live-Lizenz, die bedeutet, dass .NET Core ab sofort in Produktivszenarien eingesetzt werden kann und der Softwarehersteller aus Redmond dafür sogar Support liefert. Das RTM soll im ersten Quartal 2016 erscheinen. Damit gibt es .NET nun offiziell auf Linux, Mac und Windows. Aber nicht nur die Laufzeitumgebung und die Basisbibliotheken stehen plattformübergreifend zur Verfügung: Mit Visual Studio Code, dessen Quelltext nun ebenfalls auf GitHub liegt, gibt es eine stark wachsende IDE auf allen Plattformen. Unter der Haube benutzt Visual Studio Code die .NET Compiler Platform für die Codeanalyse und das Kompilieren von C#-Code. Der Einsatz von C# war auf anderen Plattformen bisher nur mit Mono und MonoDevelop möglich.

Die .NET Core Roadmap macht Hoffnung auf eine baldige Veröffentlichung der RTM-Version. (Abb. 1)

(Bild: Microsoft)

Mit diesen plattformübergreifenden Technologien steht die .NET-Welt und C# einer breiteren Masse an Entwicklern zur Verfügung. Das ist ein Grund mehr, sich die nächste Generation der Sprache C# einmal genauer anzusehen. Die Liste der Neuerungen, die es in die neue Sprachversion schaffen werden, steht noch nicht komplett fest. Dennoch lässt sich von den Issues im Roslyn-Repository auf GitHub ein Überblick ableiten. Aktuell gibt es 21 aktive Vorschläge für Spracherweiterungen für den C#-7-Meilenstein. Nach dem Filtern der Liste danach, zu welchen Vorschlägen es bereits eine Sprachspezifikation gibt, bleiben nur noch neun Vorschläge übrig. Auch die Design Meeting Notes, die das Team in regelmäßigen Abständen veröffentlicht, zeigen, auf welche Bereiche es den aktuellen Fokus setzt. Und nicht zuletzt lohnt sich ein Blick in den Quellcode. Dort finden sich prototypische Implementierungen einzelner Features.

Somit zeichnet sich jetzt ab, welche Sprachmerkmale ihren Weg voraussichtlich in die neue Version von C# finden werden.

Eine von der Community seit langem geforderte Erweiterung ist die Unterstützung lokaler Funktionen. Sehr oft nutzen Entwickler private Hilfsmethoden, die nur an einer Stelle Verwendung finden. Dennoch sind diese Methoden im Sichtbarkeitsbereich der ganzen Klasse implementiert. Lokale Funktionen erlauben künftig die Implementierung der Methoden an der Stelle, an der sie benötigt werden.

Als Beispiel dient eine Funktion, welche die Fibonacci-Folge berechnet. Sie kann in C# 6 folgendermaßen implementiert und aufgerufen werden:

class Program
{
static int Fibonacci(int n) => (n < 2) ? 1 : Fibonacci(n - 1)
+ Fibonacci(n - 2);
static void Main(string[] args)
{
Console.WriteLine(Fibonacci(23));
Console.ReadKey();
}
}

In C# 7 wird es möglich sein, die Fibonacci-Funktion lokal zu implementieren – im konkreten Fall also innerhalb der Main-Methode. Die Funktion ist somit ausschließlich innerhalb von Main sichtbar.

class Program
{
static void Main(string[] args)
{
int firstFibonacci = 1;
int Fibonacci(int n) => (n < 2) ? firstFibonacci : Fibonacci(n - 1)
+ Fibonacci(n - 2);
Console.WriteLine(Fibonacci(23));
Console.ReadKey();
}
}

Innerhalb der lokalen Funktionsimplementierung ist dabei der Zugriff auf Variablen möglich, die im umschließenden Sichtbarkeitsbereich definiert wurden. Das zeigt obiges Beispiel anhand der Variable firstFibonacci. Ihre Definition erfolgt im Sichtbarkeitsbereich der Main-Methode und ihre Verwendung in der Implementierung der Fibonacci-Funktion. Wäre die Fibonacci-Funktion im Scope der Klasse beziehungsweise des Typs implementiert, benötigte die Funktion die Variable als Übergabeparameter. Mit lokalen Funktionen wird das überflüssig, weil der Zugriff auf den Zustand der umschließenden Methode direkt möglich ist. Das ist schon aus Sicht der Konsistenz sinnvoll: Innerhalb von nichtlokalen Funktionen ist schließlich der Zugriff auf den Zustand des umschließenden Sichtbarkeitsbereichs gegeben, nämlich auf den der Klasse beziehungsweise des Typs.

Theoretisch wäre die Implementierung lokaler Funktionen auch mit Bordmitteln von C# 6 möglich. Unter Verwendung eines Delegaten und eines Lambda-Ausdrucks könnte das folgendermaßen aussehen:

class Program
{
static void Main(string[] args)
{
var c = 3;
Func<int, int, int> localFunction = (int a, int b) => a + b + c;
    Console.WriteLine(localFunction(1, 2));
Console.ReadKey();
}
}

Dieser Ansatz bringt jedoch Schwierigkeiten mit sich. Obiger Code unterscheidet sich bewusst von der Berechnung der Fibonacci-Zahlen, damit er überhaupt funktioniert. Der rekursive Aufruf der Fibonacci-Funktion würde an einem Lambda-Ausdruck scheitern. Der Compiler würde einen Fehler melden, wenn die Lambda-Funktion versucht, sich selbst aufzurufen, weil die Methode in dem Fall noch nicht definiert ist. Weiterhin existiert kein allgemeingültiger Delegat, der als Typ für die Variable localFunction zum Einsatz kommen darf. Entwickler müssten somit eigene Delegat-Typen für jede lokale Funktion definieren – obiges Beispiel verwendet Func<int, int, int>. Das hat auch Auswirkungen auf die Performance, weil das System für den Delegaten in dem Fall auf dem Heap ein neues Objekt erzeugt. Das bringt einen entsprechenden Overhead mit sich, der bei lokalen Funktionen nicht existiert. Mit anderen Worten: Lokale Funktionen sind schneller und effizienter.

Ein offener Punkt im Roslyn-Team ist, ob die Variable aus dem umschließenden Sichtbarkeitsbereich vor der lokalen Funktion definiert werden muss oder ob die Definition auch nach der lokalen Funktion möglich ist. Das zeigt, wie wichtig jedes Detail bei der Implementierung solcher Sprachmerkmale sein kann.