Neues von C# 6 - Teil 2 der Roslyn Saga

Im Teil 1 der Blog Serie zu Roslyn haben wir über den Neuen Kompiler Philosophiert und ich habe hoffentlich einen kleinen Eindruck hinterlassen können wie das ganze zusammenhängt. Nun ist es an der Zeit über die Sprachänderungen in C# 6 (C-Sharp) zu reden.

Die Reise begann vor etwa einem Jahr auf der Build Konferenz 2014 als Anders Hejlsberg den Roslyn Compiler vorstellte und gleichzeitig Open Source machte. Zunächst auf Codeplex und nur einige Monate später zog das Projekt um auf GitHub.

Der Grund dafür war, dass Github heute der Place to be ist, wenn es um Open Source und Community Projekte geht. Microsoft geht eine neue Wette ein. Für das Team bedeutet das, nicht mehr auf dem Campus 3 Jahre entwickeln und dann an die Öffentlichkeit zu gehen um neue Produkte und Freatures zu pushen. Von Tag eins sind die Entwickler der .net und C# Community gefragt und Features werden in einem Dialog mit den Entwicklern die Sie später verwenden sollen entwickelt.

Für mich begann die Reise zu Roslyn im Jahr 2012. Und während meines Internships habe ich diverse Gespräche, Design Sessions und Diskussionen rund um Roslyn verfolgt. Als Roslyn dann im April 2014 auf der Build-Konferenz der Öffentlichkeit vorgestellt wurde, durfte ich die Usergroups Nürnberg, Essen und Karlsruhe besuchen, um mit den Communities über die Neuerungen von C# 6 zu sprechen. Wir hatten viele gute Diskussionen über die einzelnen Features und ich habe mehrere DIN A4-Seiten mit dem Feedback aus der .NET Community gefüllt. Die Diskussionen, die ich anschließend mit der Produktgruppe hatte und die aktive Diskussion über die C# 6 Features auf GitHub haben die neue Version der Programmiersprache beeinflusst.

Die Diskussion rund um die neuen C# Features findet man auf GitHub unter Language Design Status und auf CodePlex unter Language Design Discussion.

Bevor wir uns nun den neuen Features widmen, möchte ich mich ganz herzlich bei Dustin Campbell, Lucian Wischik und Mads Torgesen und beim ganzen C# Language Team, das einen großartigen Job gemacht hat und uns regelmäßig über die neuen Features informiert hat, bedanken.

Die neuen Features in C# 6

1. Using static

Dieses Feature erlaubt das Spezifizieren einer statischen Klasse im "using"-Statement. Dieses Feature stellt alle sichtbaren oder besser zugreifbaren (ihr wisst schon: "public", "internal", ...) statischen Member einer Klasse zur Verfügung.

Was ist damit gemeint? Die VB Entwickler kennen dieses Feature bereits länger. Schauen wir mal auf die Ausgabe der System.Math.PI Konstante auf der Konsole.

Vorher:Abbildung 1.1

Wir sehen hier die explizite Schreibweise eines Aufrufes auf die Konsole über "System.Console.WriteLine()" und den Wert, der übergeben werden soll: "System.Math.PI".

Mit dem "using static"-Statement kann man diesen Code nun vereinfachen.

Edit:Abbildung 1.2

In Zeile 3 und 4 führe ich die statischen using-Statements „System.Console“ und „System.Math“ hinzu.

Neu in der Visual Studio 2015 CTP ist nun die Glühbirne, die besonders die ReSharper-Benutzer kennen. Hinter der Glühbirne verstecken sich viele hilfreiche Tipps und Tricks, wie man seinen Code verbessern kann.

In diesem Fall schlägt mir Visual Studio vor, den Aufruf der Member von „System.Console“ und „System.Math“ zu vereinfachen. Ich habe die Möglichkeit, eine Vorschau zu verwenden oder direkt die Änderungen zu übernehmen. Die Visual Studio 2015 IDE bietet dank Roslyn und der Syntax APIs sowie der Syntax Transformation APIs genau diese Vorschaufenster an. Mehr dazu finden Sie in Teil 1 dieser Blogpost-Serie.

Nachher:Abbildung 1.3

Der fertige Code sieht nun ziemlich kompakt aus. Und da ich hier keine explizite Typkonvertierung brauche, fällt auch die "ToString()"-Methode weg.

Einsatz: Bietet sich beim Arbeiten mit mehreren Aufrufen auf statische Member einer Klasse an, z. B. „System.Math“ oder Fremdhersteller.

1.1 Erweiterungs Methoden

Ein weiterer Fall, bei dem dieses Feature hilfreich sein kann, sind die Extension Methods die mit .NET 3.5 eingeführt wurden. Dabei handelt es sich um statische Methoden, die aber als Methoden einer spezifischen Instanz verwendet werden können. Anstatt die Erweiterungs-Methoden in den globalen Gültigkeitsbereich (Scope) zu laden, erlaubt die "using static"-Funktion das Erweitern des korrespondierenden Typen um die passende Erweiterungs-Methode.

Vorher:Abbildung 1.4

In diesem Beispiel wird ein Array vom Typ „Points“ erzeugt, das X- und Y-Koordinaten enthält. Als erstes führe ich die "static using"-Elemente ein. Dadurch können wir, wie unter Punkt 1 beschrieben, „System.Console“ bei der Anweisung „Console.Writeline“ loswerden. Das gleiche mache ich noch einmal für „System.Math“.
Der Unterschied betrifft nun Zeile 4. Hier wird das "using"-Statement:
using System.Linq; verwendet, was den Scope unserer Klasse um alles im Namespace "System.Linq“ erweitert. In Zeile 106 verwenden wir die Extension-Methode von: System.Linq.Enumerable;
um einen Export aller Punkte im Array "Points" durchzuführen und diese in JSON-Elemente zu konvertieren.
Die Methode „ToJson()“ lebt in der Klasse "Points", auf die wir gleich zu sprechen kommen. Statt den kompletten Scope von „System.Linq“ zu laden, reicht es in diesem Fall, den Scope von System.Linq.Enumerable; und die darin enthaltenen Erweiterungs-Methoden zu laden.
Edit:Abbildung 1.5

Dazu kommentiere ich Zeile 4 aus und füge in Zeile 11 das folgende "using static"-Statement hinzu:
using static System.Linq.Enumerable; An unserem Aufruf in Zeile 111 ändert sich nicht viel, es funktioniert wie erwartet weiter. Aber wir haben den Scope von „System.Linq“, der geladen wird, entsprechend verkleinert. Und uns stehen nun die entsprechenden Extension-Methoden von „Enumerable“ zur Verfügung, bspw. “Range“, das man hier im IntelliSense-Fenster in Zeile 112 sehen kann.

Nachher:Abbildung 1.6

Das Nachher-Bild sieht genauso aus wie zuvor, hier findet nur eine Einschränkung auf den Scope von „System.Linq“ statt.

Veranschaulichen wir uns nochmal den Einsatz der Erweiterungs-Methoden mit dem using static-Features. In der Dokumentation unter Punkt 3.1 finden wir den folgenden Beispiel-Code:
Abbildung 1.7

AAuch hier kommentiert man das
using System.Linq; Statement aus. „Range“ liegt im Scope von „System.Linq.Enumerable“, ist aber keine Extension-Methode, sondern eine Klasse, die in diesem Namespace liegt, weshalb man sie entsprechend verwenden kann. Man erhält jedoch sofort die Fehlermeldung, dass „Where“ in Zeile 16 nicht mehr im Scope sei. Wo hingegen die Extension-Methode „range.Where“ in Zeile 17 im Scope liegt, da es sich um die Extension-Method der „Enumberable“-Klasse handelt.

Dieses Beispiel verdeutlicht sehr schön, welchen Einfluss using static auf den Scope und die Extension-Methoden hat. Kurz gesagt, wir können die Teile, die wir benötigen, noch granularer verwenden und andere Dinge im übergeordneten Namespace getrost vergessen.

Achtung !!! Es kann jetzt ein "breaking change" sein, eine gewöhnliche statische Methode in eine Extension-Methode zu verwandeln. Dies war bisher nicht der Fall, da man diese explizit deklarieren musste. In den meisten Fällen jedoch werden Extension-Methoden als gewöhnliche statische Methoden aufgerufen. In den wenigen Fällen, in denen es jetzt eine Zweideutigkeit geben kann, sollte man ohnehin das explizite Deklarieren von Extension-Methoden verwenden.

2. Auto Property

Properties oder für Java-Entwickler Getter und Setter sind eine Kernkomponente der Objektorientierten Programmierung.
Abbildung 2.1Abbildung 2.1

Diese Abbildung zeigt den klassischen Property-Ansatz (Person.Name) und den Auto-Property-Ansatz von C# 3.0 (Person.Surname).

In C# 6 werden zwei Änderungen eingeführt, die Auto-Properties betreffen.

2.1 Initializer für Auto-Properties

C# 6.0 bringt die Initialisierung von Auto-Properties mit, die genau so funktionieren, als würde man ein privates Feld initialisieren (siehe Zeile 27 in Abbildung 2.2).
Abbildung 2.2Abbildung 2.2

Man muss wissen, dass hier einiges an Compiler-Magie verwendet wird. Wann immer man dieses Feature verwendet, wird die Microsoft .NET Compiler Platform (aka Roslyn) in der Microsoft Intermediate Language eine Property mit Backingfield erzeugen (siehe Abbildung 2.1). Die Initialisierung von „Surname“ wird also wie folgt ausgeführt.
Abbildung 2.3

In Abbildung 2.3 habe ich den Fehler, dass mein Nachname "Patric" sei, behoben. Was beide oben beschriebenen Wege tun? Genau das gleiche.
Es ist wichtig zu verstehen, dass die Initialisierung der Property nicht über den "Setter" funktioniert, da hier z.B. bei Konzepten wie MVVM ein "PropertyChanged"-Event ausgeführt werden könnte. Dies ist aber nicht der Fall, da der Wert direkt dem Backingfield zugewiesen wird. Genau wie bei "field-Initializers", also der Initialisierung von privaten Feldern (Abbildung 2.3, Zeile 19), kann man hier das Keyword "this" (Referenz auf Klasse) nicht verwenden. Der Grund dafür ist, dass die Initialisierung der Felder vor der Initialisierung des Objektes bzw. der Klasse durchgeführt wird, weshalb "this" zu diesem Zeitpunkt noch nicht existiert.

Diese Funktionalität wäre insbesondere im Zusammenspiel mit dem (Primary Construktor) Primären Konstruktoren Konzept interessant geworden. Da man direkt im Kopf der Klasse Parameter initialisieren und den AutoProperties zuweisen hätte können. Das hätte tatsächlich einiges an Überflüssigen Code reduziert. Die Nachteile der Primären Konstruktoren wären aber ein schwierigeres Refactoring, Unübersichtlichkeit und auch die Einschränkungen in der Erweiterbarkeit im Sinne von Konstruktorüberladung gewesen. Das Feedback und die wirklich guten Gespräche mit der Community haben dazu geführt, dass die Primären Konstruktoren es nicht in C# 6.0 geschafft haben.

2.2 Getter-only auto-properties

Bevor wir uns dieser Thematik annehmen, müssen wir die Begriffe "Immutable" und "Mutable" verstehen. Bei einem "Immutable Type" handelt es sich um einen Typen, dessen Werte nicht mehr geändert werden können, wenn er einmal initialisiert ist.

immutable type A type whose instance data, fields and properties, does not change after the instance is created. Most value types are immutable.
MSDN Definition

Immutable Types spielen häufig in der nebenläufigen Programmierung eine entscheidende Rolle. Eric Lippert hat eine sehr interessante Serie von Blog Posts zu diesem Thema geschrieben.

Ein Problem war es bisher, das Konzept von Immutable Types mit den Auto-Properties umzusetzen. Eine Möglichkeit war es, den Setter "private" zu machen, was aber nur etwas an der Sichtbarkeit bzw. Zugriffsberechtigung änderte (siehe Abbildung 2.4).
Abbildung 2.4

In C# 6.0 kommt nun eine neue Funktionalität hinzu: Getter-only Auto-Properties. Der Name verrät auch schon, worum es dabei geht. Anstelle des in Abbildung 2.4 verwendeten Patterns kann man nun Auto-Properties wie in Zeile 23 (Abbildung 2.5) definieren:
Abbildung 2.5

Um zu sehen, was dahinter steckt, habe ich in Zeile 28 - 33 die Property "ImmutableName" erzeugt. Verwendet man die "Getter-only Auto-Properties", so muss man in Zukunft keinen Setter mehr deklarieren. Das hat zur Folge, dass das Backing-Field in Zeile 30 automatisch als readonly definiert ist.

Das Ganze funktioniert auch mit der neuen Funktionalität AutoProperty-Initializer, die wir unter Punkt 2.1 besprochen haben.
Abbildung 2.6

Außerdem kann man die "getter-only property" auch im Konstruktor der Klasse verwenden. Auch hier genau wie bei der Property-Initializer Funktionalität wird der Wert nicht über den Setter, sondern direkt dem darunter liegenden Feld (Backing-Field) zugewiesen.
Abbildung 2.7

Dieses Feature erlaubt es, Typen kurz und knapp auszudrücken. Es soll den Code übersichtlicher machen und den Entwickler nicht überfordern. Das war ganz allgemein das Kredo des C# 6.0 Language Design Teams.
Anfangs hatten wir über "Mutable" und "Immutable" Types gesprochen. Es ist wichtig zu verstehen, dass dieses Feature einen wichtigen Unterschied zwischen mutable und immutable Types entfernt: Auto-Properties standen bisher nur zur Verfügung, wenn man seinen Typen "mutable" deklarieren wollte. Das ändert sich in C# 6 und man kann nun auch durch Getter-only Auto-Properties Immutable Types definieren.

3. Expression bodied function members

3.1 Expression bodies on method-like members

Unter 1.1 habe ich eine simple Konsolenapplikation angelegt, die Punkte als JSON serialisieren kann. Dazu gibt es eine Klasse "Points", in der ich die "ToString"-Methode überlagere.

Vorher: Abbildung 3.1 - Überschreiben der ToString() Methode vorher

Ein neues Feature in C# 6 sind Expression-bodied Methods. Was bedeutet das? Nehmen wir mal an, wir haben eine Methode wie "ToString()", die genau eine Zeile hat, in der ein Ausdruck via Return zurückgegeben wird. Dann erlaubt es C# 6, dies mithilfe des Pfeils, den wir schon von Lambdas kennen, zu machen. Es handelt sich dabei zwar nicht um ein Lambda aber die Verwendung fühlt sich genauso an.

So sieht das Ganze mit der neuen kürzeren Schreibweise aus.

Nachher: Abbildung 3.2 - Überschreiben der ToString() Methode nachher

Man spart sich hier sowohl das "return"-Statement als auch die Klammern. Dies bietet sich natürlich insbesondere für einfache Methoden an, die in einer Zeile etwas berechnen und zurückgeben.

Auch für Methoden des Types "void" - also die reine Ausführung ohne Rückgabewerte - und asynchrone Methoden, die "Task" zurückgeben, steht die Pfeil-Syntax zur Verfügung. Allerdings muss die Expression eine "statement"-Expression sein, genau wie das bei Lambdas der Fall ist. Zeile 19 zeigt den Einsatz der Expression "Bodies" bei einer statischen "void"-Methode.

Abbildung 3.3

3.2 Expression bodies on property-like function members

In der Klasse "Point" gibt es die Property "Dist", welche die Distanz zwischen X und Y berechnet und nach dem gleichen Schema wie die Methoden oben arbeitet.

Vorher: Abbildung 3.4

Die Idee hier ist, dass wir wie bei Tasks einen Expression Body verwenden können und der "Autocalculated"-Property die überflüssigen Klammern und das "return" nehmen.

Nachher: Abbildung 3.5

Ein weiterer Einsatzmöglichkeit für die Expresssion Bodies sind die sogenannten Autocalculated Properties. Autocalculated Properties sind Properties, die als Resultat eine Berechnung oder eine Operation durchführen.

Ein Beispiel dafür ist die Property "FullName" in Abbildung 3.6.

Vorher:Abbildung 3.6

Auch hier lässt sich der Lambda-Pfeil einsetzen, um auf eine kürzere Schreibweise zu kommen.

Nachher: Abbildung 3.7

Die Funktionalität ist dieselbe wie zuvor bei den Methoden.
Gerade zu diesem Feature gab es sehr viel Diskussion - sowohl online als auch offline. Zu komplex, für Anfänger nicht geeignet, lautete im Großen und Ganzen das Feedback. Ich persönlich komme aus der C/C++-Ecke und bin ein großer Fan der imperativen prozeduralen Programmiersprachen mit der Geschmacksrichtung "objektorientiert". Was wir gerne mal an der Bar genauer diskutieren können. Ich kann den Lambda-Pfeilen bei einfachen Ausdrücken ziemlich viel abgewinnen und freue mich darauf. Aber ich kann auch die Argumente verstehen, die hier ins Feld geführt werden.

4 String Interpolation

Von allen neuen Features in C# 6 ist dieses wahrscheinlich am Populärsten. "String.Format" und seine Verwandten sind unglaublich nützliche Werkzeuge und ein wesentlicher Vorteil der "Managed Programmiersprachen."

Ich erinnere mich noch an das legendäre Battle zwischen Michael Kaplan und Raymond Chen.

Aber die Verwendung von String-Literals ist sperrig und fehleranfällig. Ein bisschen unglücklich ist die Verwendung der Platzhalter "{0}" in "Format.String", die in derselben Reihenfolge wie die Argumente stehen müssen, was die folgende Abbildung illustriert.
Vorher: Abbildung 4.1

Das Konzept der String Interpolation ist nicht neu. Aber die Möglichkeit, Variablen genau da einzusetzen, wo sie stehen sollen, war bisher nur über den StringBuilder oder temporäre Strings (größerer Memory Footprint) abbildbar. In C# 6.0 sieht das nun anders aus.

Nachher: Abbildung 4.2 String Interpolation in C# 6.0

Wie man hier sehr schön sehen kann, wird an den Beginn des String einfach das "$"-Zeichen gesetzt.
Sofort werden die durch den Platzhalter gekennzeichneten Regionen farblich hervorgehoben. Und anstelle der Platzhalter-Indexer kann man nun direkt die Variablen oder Typen verwenden, die man für die Ausgabe benutzen möchte.

Ein weiterer Vorteil ist, dass man hier auch eine String-Formatierung anhängen kann.
Abbildung 4.3

Und hier kommt das eigentliche Highlight der neuen String Interpolation: Denn genau wie bei den Indexern kann an die Stelle des Platzhalters fast jeder Ausdruck gestellt werden. Abbildung 4.4 zeigt wie man dank des bedingten Operators eine Fallunterscheidung auf das Alter durchführen kann. Abbildung 4.4

WriteLine($"{p.Name,20} is {p.Age:D3} year{(p.Age == 1 ? "" : "s")} old");

In Verbindung mit der neuen Funktion der Expression-Bodies (Punkt 3.1) bei Methoden macht das ganze richtig Spaß. Das Überladen der "ToString()"-Methode kann in Zukunft also wie folgt aussehen.
Abbildung 4.5

public override string ToString() => String.Format($"pt({X},{Y})");

Ein absolut cooles Feature, das einem viel Zeit sparen wird.

5 Null-conditional operators

Okay, bei diesem Feature habe ich mir oft den Mund fusslig geredet. Ich gebe auch zu, dass mein Szenario ziemlich komplex war. Sorry dafür. Ich habe daher beschlossen, das Ganze ein wenig detaillierter zu beschreiben.

Wir alle kennen folgendes Szenario: Wir wollen die Länge eines Arrays abfragen. Natürlich müssen wir vorher überprüfen, ob es nicht null ist.

Vorher:Abbildung 5.1

Der Null-conditional Operator ermöglicht folgende Vereinfachung.
Nachher: Abbildung 5.2

Die Magie liegt in Zeile 19:
int? length = pA?.Length;

Wir erzeugen ganz C-like einen Nullable Integer, also eine Variable, die Null oder eine Ganzzahl repräsentieren kann, da wir nicht wissen, ob dieses Array initialisiert wurde oder Werte enthält. (Okay, hier können wir uns sicher sein =). ) Benutzen wir das "?" direkt nach dem Array gefolgt von dem Wert, den wir eigentlich haben wollen, nämlich die Länge des Arrays. Das spart uns das komplette Validieren des Arrays auf Null. Unsere Variable "int? length" ist entweder "null", nämlich genau dann, wenn das Array null ist, oder sie hat den Wert der Länge des Arrays.
Damit lassen sich auch andere Dinge umsetzen. Ein Beispiel:

Damit lassen sich auch andere Dinge umsetzen. Ein Beispiel:
Person person = pA?[0];

In dieser Zeile wird "Person" der erste Wert des Arrays zugewiesen oder null, wenn das Array null ist.

Im Umgang mit dem Null-conditional Operator ist zu beachten, dass er ein kürzendes Verhalten hat. Was ist damit gemeint? Ähnlich wie beim Ausdruck

A && B && C

Wenn A bereits Falsch ist, wird B oder C gar nicht mehr geprüft. Genau dieses Verhalten gilt auch für den Null-conditional Operator.

int? first = Person?[0].Name.Length();

Wenn "Person" null ist, werden der Aufruf und die Unteraufrufe erst gar nicht ausgeführt.

Dieses Beispiel entspricht also:
int? first = (customers != null) ? customers[0].Orders.Count() : null;

5.1 Null-conditional und Null coalescing operator ??

Typischerweise wird der Null-conditional Operator mit dem null coalescing-Operator verwendet.

int length = pA?.Length ?? 0;

In diesem Fall wird wie in Abbildung 5.2 die Länge des Arrays abgefragt. Allerdings setzt man hier entweder die Länge des Arrays oder, wenn das Array null ist, die Länge "0" als Ganzzahlwert.

Das Ganze ist also das gleiche wie:
int? first = (pA != null) ? pA[0].Name.Length() : null;

Natürlich können Null-conditional Operator auch aneinander gereiht werden.
int? first = pA?[0].Name?.Length();

Wenden wir das Ganze auf unsere "Point"-Klasse an. Die Methode "FromJson" nimmt ein JObject entgegen und gibt einen Point zurück. JObject muss natürlich vor dem Zugriff auf null gecheckt werden. Und auch die Konvertierung von x und y benötigt einen entsprechenden Check.
Vorher: Abbildung 5.3

Wenden wir den Null-conditional Operator darauf an, erhalten wir folgenden Code.
Nachher:Abbildung 5.4

Wir sparen uns hier 3-mal den Check gegen Null und werden den einen oder anderen Knoten im Kopf los. Allerdings checken wir hier nun 2-mal auf Null anstatt wie bisher nur einmal, wenn bereits json null wahr. Können wird das mithilfe des Compilers optimieren? Schwierig, da "json" eine lokale Variable ist. Aber nach Rücksprache mit dem JIT Team ist der produzierte Overhead gar nicht so groß und sollte keine merklichen Performanceeinbrüche generieren.

Das ganze Pattern wird richtig spannend, wenn wir and die PropertyChanged EventArgs denken. Zwar können wir keine parameterisierte Argumentenliste direkt nach dem conditional Operator triggern. Aber mithilfe der "Invoke"-Methode des Delegaten ist dies möglich, was den Einsatz von beliebten Patterns wie MVVM erleichtert.
PropertyChanged?.Invoke(this, args); Das beste an diesem Feature ist aber, das es einen Weg beschreibt Thread-safe ein Argument auf null zu checken, bevor wir einen Event triggern.

DDas Beste an diesem Feature ist aber, dass es einen Weg beschreibt, Thread-safe ein Argument auf null zu checken, bevor wir einen Event triggern.
Der Nachteil dieser Variante ist allerdings, dass der Code dadurch kompakter wird und das für das Debugging weniger Haltepunkte zu setzen sind. Dank Roslyn können wir hier allerdings in Zukunft besseres Tooling erzeugen, das gerade für den kompakten Code explorative bzw. Debugger-spezifische Informationen schöner aufbereitet. Das ist allerdings nicht im aktuellen Scope von Visual Studio 2015.

6 Index Initializers

Zurück zur "Pointer"-Klasse. Hier finden wir den umgekehrten Weg wie in Abbildung 5.1 beschrieben.
Vorher: Abbildung 6.1

In C# 5.0 muss man wie in Abbildung 6.1 beschrieben zunächst das Objekt erstellen (Zeile 43). Anschließend kann man via Indizes die Werte von X und Y separat zuweisen. Danach erst kann man den Wert zurückgeben.

Wenn X und Y richtige Member gewesen wären, hätte man dafür den Objekt-Initializer vverwenden können. Was ich machen möchte, ist in Abbildung 6.2 beschrieben.
Abbildung 6.2

In C# 6.0 geht das jetzt anders. Während bisher der Scope des Object Initializers auf Member beschränkt war, kann man nun auch Indizes initialisieren. Das geht natürlich nur, wenn die entsprechenden Indizes auch zur Verfügung stehen.

Nachher: Abblidung 6.3

Dadurch wird unser Code wieder einmal kompakter und ausdrucksstärker. Aber wer genau hinschaut, sieht schon die nächste Optimierungsmöglichkeit, die wir dank der in Punkt 3 beschriebenen Expression-Bodies-Funktionalität erreichen können. Denn durch die Optimierung der Index Initializers sind wir auf das Single-Return Statement zurückgefallen und können das jetzt entsprechend direkt zurückgeben.
Abbildung 6.4

So erreichen wir eine Reduktion des Codes von 4 Zeilen Datenstecken auf genau 1 Zeile deklarative Initialisierung.
Das Ganze lässt sich natürlich auch auf ein Array oder Dictionary mit numerischen Indizes projizieren.
Auch hier sei gesagt, dass der Debugger kein Freund der Object Initializer ist und bisher leider nur spärlichen Support anbietet.

7 Nameof

Um Nameof zu erklären schauen wir auf die Methode GetJArrayAsync der Pointer Klasse.
Abbildung 7.1

Diese Methode soll, wenn der Pfad, den Sie bekommt, null ist, eine Exception werfen. Aber "path" ist nicht gerade die beste Beschreibung, weshalb ich das Visual Studio Refactoring Rename verwenden möchte. Dazu markiere ich "path" und drücke F2.
Edit Abbildung 7.2

Visual Studio hebt mir die Orte im Code, an denen "path" vorkommt, farblich hervor. Wer Teil 1 dieses Beitrags über Roslyn gelesen hat, weiß in etwa, wie das funktioniert. Noch während ich tippe, erhalte ich von Visual Studio eine Einsicht, wie sich mein Code verändert.
Edit: Abbildung 7.3

Schon jetzt springt mir ins Auge, dass diese Operation einen Fehler produzieren wird. "Throw new ArgumentNullException" nimmt als Parameter den String "path" entgegen, den ich gerade umzubenennen versuche. Das ist an dieser Stelle nicht kritisch, aber schade. Ich breche den Vorgang ab. Der "nameof"-Operator soll genau für dieses Problem die Lösung sein.

Zeile 45 Vorher:Abbildung 7.4.1

Zeile 45 Nachher:Abblidung 7.4.2

Statt das Objekt händisch via "String" zu übergeben, verwende ich den "nameof"-Operator. Wenn ich jetzt das Inline-Rename Refactoring aufrufe, sehen wir sofort den Vorteil.
Abbildung 7.5

Visual Studio sorgt nun dafür, dass bei Rename auch der Parameter in der "ArgumentNullException" entsprechend angepasst wird.

Denken wir mal an die C# 5.0-Variante von "OnPropertyChanged", die gerade bei MVVM sehr häufig zum Einsatz kommt.

C# 5.0 OnPropertyChanged Implementierung Abbildung 7.6.1

Was wir sehen können, ist genau dasselbe Verhalten wie in Abbildung 7.4.1. Nur dass hier ein Rename und der Parameter in Zeile 13 und 14 wirklich störend sind. Denn führe ich ein Rename der Property durch, hat das einen deutlich größeren Einfluss als oben. Oben hätte in der Exception ein falscher Wert gestanden. Nicht weiter tragisch. Hier aber würde meine gesamte Logik nicht mehr funktionieren.
Dieses Szenario zeigt sehr schön den Mehrwert des "nameof"-Operators.

C# 6.0 OnPropertyChanged Implementierung Abbildung 7.6.2

8 Await

Ich glaube, dass wir, was das Konzept Async und Await anbelangt, noch einiges an Ressourcen bereitstellen müssen. Hier herrscht immer noch große Verwirrung in der Entwicklergemeinde. Vielleicht schreibe ich hierzu ebenfalls eine Mini-Serie. Feedback ist herzlich willkommen.
Twitter: @patricmsdn

Ein Problem in C# 5.0 war bisher die Problembehandlung. Dies betraf zum Beispiel den Einsatz von "await" im "Try/Catch/Finally"-Block, da weder im Catch- noch im Finally-Block der Einsatz des Keywords "await" erlaubt war. Das gehört nun der Vergangenheit an und man kann somit auch asynchrone Calls im Catch- und/oder Finally-Block verwenden.

Bleiben wir bei der "GetArrayAsync"-Methode der "Pointer"-Klasse. Hier kann ich nun sowohl im Catch- als auch im Finally-Block das Keyword verwenden, wie in Zeile 55 und 59 in Abbildung 8.1 zu sehen ist.

Abbildung 8.1

9 Exception Filters

Filter für Exceptions gibt es bereits bei F# und VB.NET. Für C# 6 sind sie jedoch ein neues Konzept. Die Idee dabei ist, eine Exception zum Beispiel nur dann zu fangen, wenn der Ausdruck (Filter) "wahr" zurückgibt. Anderenfalls wird der Catch-Block nicht ausgeführt. In der Visual Studio 2015 CTP sieht das Ganze wie folgt aus:
Abbildung 9.1

Der Vorteil von Exception Filters ist, dass man Exceptions fangen und erneut werfen kann, da Sie den Stacktrace nicht beeinflussen. Wenn eine Exception zu einem späteren Zeitpunkt geworfen wird, kann man über den Stacktrace immer noch nachvollziehen, woher sie ursprünglich kam.
Üblicherweise werden Exception-Filters missbraucht, um beispielsweise Logging durchzuführen. Man kann außerdem eine Exception inspizieren, ohne ihren Weg durch das Programm zu unterbrechen. Eine Hilfsfunktion wird oft innerhalb des Filters aufgerufen, um Dinge durchzuführen und "false" zurückgeben, um genau den beschriebenen Effekt zu erreichen.
Beispiel für Hilfsfunktion:
private static bool Log(Exception e) { /* log it */ ; return false; }

Aufruf der Hilfsfunktion:
try { … } catch (Exception e) if (Log(e)) {}

10 Parameterlose Konstruktoren in structs

Structs kommen in Managed Code Programmiersprachen eher seltener vor. Sind aber ein beliebtes Konzept in der Spieleentwicklung und bei Programmen die Bildbearbeitung ermöglichen. Es gibt einen ganz netten Artikel auf MSDN zum Thema wann man Structs verwenden sollte, und wann nicht.

In C# 6.0 bekommen die Structs nun einen Parameterlosen Konstruktur.

Zusammenfassung

Das waren Sie auch schon die Neuerungen in C# 6.0. Alles in allem haben wir in der CTP 5 von Visual Studio 2015 keine schicken neuen Effekte für C# 6 zur Verfügung gestellt, sondern uns darum bemüht, den Alltag des Entwicklers zu verbessern. Weniger Code für bessere Resultate.
Einer der Hauptkritikpunkte war, wie neue Teammitglieder mit den Änderungen zurechtkommen sollen. Nun dafür haben wir uns auch eine Lösung ausgedacht. Aber die verrate ich erst in Teil 3 der Serie, da Sie mit den Diagnostics APIs zusammenhängt.