Neue Sprachfeatures für C#

Sprachen  –  47 Kommentare

Im Rahmen der BUILD-Konferenz in San Francisco hat Microsoft eine Vorabversion einiger neuer Sprachfeatures für die Programmiersprache C# veröffentlicht. Die neue C#-Syntax ist Bestandteil der .NET Compiler Platform "Roslyn", die nun den Stand einer "End User Preview" hat und von ihrem Schöpfer Anders Hejlsberg zum Open Source-Projekt erklärt wurde.

Noch steht nicht fest, wann und welche der in dieser Preview gezeigt Features Einzug in .NET halten werden, zumal das Unternehmen bisher keine Roadmap für das nächste, vermutlich mit Version 5.0 versionierte .NET Framework und ein zugehöriges C# 6.0 bekannt gegeben hat.

Initialisierung für automatische Eigenschaften

Initialisierung für automatische Eigenschaften (engl. Automatic Property) gibt es in C# seit Version 3.0 (2008) und Visual Basic seit Version 10.0 (2010). Bisher war es bei automatischen Eigenschaften nicht möglich, diese in der Deklaration schon zu initialisieren. Das soll nun in C# nachgerüstet werden. Die beiden folgenden Zeilen zeigen eine Initialisierung mit einem statischen Wert und einem Ausdruck:

public int Version { get; set; } = 1;
public byte Status { get; set; } = status.HasValue ? status.Value : 1;

Diese Initialisierer können aber – genau wie bei Feldern – nicht die Referenzierung this verwenden. Auch ist es so möglich, eine automatische Eigenschaft ohne Setter zu schaffen, also eine automatische Eigenschaft, die nur gelesen werden kann (Read-Only Automatic Properties):

public DateTime ErzeugtAm { get; } = DateTime.Now;

Primärkonstruktoren

Ein primärer Konstruktor erlaubt die Deklaration eines Konstruktors schon im Rahmen der Klassendefinitionszeile:

class Kunde(string name, int? status, string ort)

Die im primären Konstruktor verwendeten Parameternamen können zunächst nur zur Initialisierung von Feldern und Eigenschaften zum Einsatz kommen. Wenn man allerdings public, protected oder private davor schreibt, werden die Parameter automatisch zu entsprechenden Feldern der Klasse. Im folgenden Beispiel ist der Parameter name nur zur Initialisierung erlaubt, wohingegen ort auch in Methoden der Klasse und auch von außen benutzbar ist. Leider ist es bisher nicht möglich, dass ein Parameter eines primären Konstruktors auch zur Eigenschaft der Klasse wird. Das ist insofern bedauerlich, dass einige Bibliotheken im .NET Framework nur mit Eigenschaften, nicht aber mit Feldern zusammenarbeiten.

class Kunde(string name, byte? status, public string ort)
{

public string Name { get; set; } = name;
public int Version { get; set; } = 1;
public byte Status { get; set; } = status.HasValue ? status.Value : (byte)1;
public DateTime ErzeugtAm { get; } = DateTime.Now;

// Deklaration nicht möglich: ort durch Primary Constructor bereits
// vorhanden!
// string ort;

public string GetInfo()
{
// nicht möglich:return name;
return Name + " aus " + ort;
}
}

Neben dem primären Konstruktor kann es weitere Konstruktoren in einer Klasse mit Primärkonstruktor geben, die aber alle den Primärkonstruktor oder einen anderen Konstruktor der Klasse aufrufen müssen.

// Klasse mit Primärkonstruktor
class Kunde(string name, byte? status, public string ort)
{
// Weitere Konstruktoren müssen Primärkonstruktor oder anderen
// Konstruktor aufrufen
public Kunde() : this("unbekannt", 0, "unbekannt")
{

}
public Kunde(string name, byte status) : this(name, status, "")
{

}

public Kunde(string name) : this(name, 1)
{

}
}

Using für statische Mitglieder

Das using-Schlüsselwort wird in C# bisher schon außerhalb von Klassen für die Einbindung von Namensräumen und Erweiterungsmethoden verwendet. Nun kommt eine weitere Einsatzmöglichkeit hinzu, die zwar eine deutliche Verkürzung der Schreibweisen erlaubt, aber auch als einen Rückfall in die Zeit des prozeduralen Programmierens angesehen werden kann: Bei einem using-Befehl unter Angabe eines Klassennamens werden alle statischen dieser Mitglieder so eingebunden, dass ein Aufruf ohne Klassenname möglich ist.

using System.Console;
using System.Environment;

...

// Zugriff möglich auf statische Attribute und Methoden ohne
// Klassennamen wegen using
WriteLine(UserDomainName + @"\" + UserName);
ReadLine();

Deklaration innerhalb von Ausdrücken

Variablendeklarationen sind im neuen C# nun auch innerhalb von Ausdrücken möglich. Microsoft nennt das Feature "Declaration Expressions". Diese sind sehr hilfreich für out-Parameter, zum Beispiel beim Einsatz der TryParse()-Methoden:

if (Int32.TryParse("1", out int zahl))
{
Console.WriteLine("Zahl ist " + zahl);
}

Filter für Laufzeitfehler (Exeption Filters)

In Visual Basic war es bisher schon möglich, in einem beim Anfangen von Fehlern mit catch neben einem Fehlertyp auch noch einen Bedingungsausdruck anzugeben. Dies kann C# jetzt auch:

int x = 0;
try
{
int z = 100 / x;
}
catch (Exception ex) if (x == 0)
{
Console.WriteLine("Division durch 0: " + ex.Message);
}
catch (Exception ex) if (x != 0)
{
Console.WriteLine("Anderer Fehler: " + ex.Message);
}

Index-Initialisierung

Mit C# 3.0 hatte Microsoft Objekt- und Objektmengeninitialisierer eingeführt. Nun kann der C#-Entwickler ein Objekt mit Indexer auch mit selektiven Werten initialisieren:

var PLZListe = new Dictionary<int, string>
{
[45127] = "Essen",
[30625] = "Hannover",
[69123] = "Heidelberg"
};

Console.WriteLine(PLZListe[45127]);

Await in catch und finally

Das in C# 5.0 eingeführte Schlüsselwort await durfte bisher nicht im catch- und finally-Block einer try-Anweisung enthalten sein. Dies soll im kommenden C# erlaubt sein.

Resource res = null;
try
{
res = await Resource.OpenAsync(…);
...
}
catch(ResourceException e)
{
await Resource.LogAsync(res, e);
}
finally
{
if (res != null) await res.CloseAsync();
}

Weitere Features

In der Dokumentation der Roslyn End User Preview wird auch noch eine neue Schreibweise für Binärzahlen (int Zahl = 0b11110000) sowie eine Gruppierungsmöglichkeit für große Dezimalzahlen (int Zahl3 = 123_456) erwähnt. Beide Syntaxformen sind aber derzeit noch nicht im Compiler implementiert.

Auf der Roslyn-Projektseite diskutiert das Microsoft-Entwicklungsteam zudem noch weitere Feature wie ?. als sogenannten "Null-propagating operator". Mit ihm könnte man die Prüfung auf Null-Werte stark verkürzen, denn ?. liefert null in Fehler zurück, bei denen es bisher eine NullReferenceException gab. Aus dem Zweizeiler

var kunde = GetKunde();
string name = (kunde == null) ? null : kunde.Name;

würde somit der knappe Einzeiler:

var name = GetKunde()?.Name;

Diese und andere Themen sind noch nicht ausdiskutiert. Auch in der Dokumentation der hier vorgestellten Sprachfeatures wirft der Autor Mads Torgersen auch immer selbst wieder Zweifel an der aktuellen Implementierung auf und fordert die Benutzer auf, Microsoft Rückmeldung zu geben über die Sprachfeatures. Es ist also noch vieles im Fluss. (ane)

Dr. Holger Schwichtenberg
leitet das Expertennetzwerk www.IT-Visions.de, das Beratung, Schulungen und Softwareentwicklung im .NET-Umfeld anbietet. Er hält Vorträge auf Fachkonferenzen und ist Autor zahlreicher Fachbücher.