Thomas Kramer

IT-COW | 4.0

Neue Sprachfeatures in C# 4.0

By Administrator at August 16, 2010 21:01
Filed Under: .net-Framework, 4.0, C#

Ich befasse mich gerade mit den neuen Sprachfeatures von C# 4.0. Gemäß Herrn Golo Roden im Heise Developer-Blog gibt es nur drei neue Sprachmerkmale, die diese neue Version mit sich bringt. Dabei handelt es sich um Optionale und benannte Parameter, Dynamische Datentypen und Ko- und Kontravarianz von generischen Datentypen.

 

Da konkrete Beispiele oft einfacher zu verstehen sind als komplizierte Beschreibungen, habe ich dazu jeweils Codebeispiele (in C#) herausgesucht.

 

1. Optionale und benannte Parameter

 

In seinen Codequalität-Webcasts hatte Herr Roden bereits von den Optionalen|Default-Parametern von C# 4.0 abgeraten. Der entsprechende MSDN-Eintrag ist hier verlinkt, ein Beispiel für eine Methodendeklaration mit einem optionalen Parameter sieht demnach folgendermaßen aus:

 

public void ExampleMethod(int required, string optionalstr = "default string", int optionalint = 10);

 

Wie man sieht werden Default-Werte bereits in der Methodendeklaration zugewiesen. Wenn man den zweiten Parameter beim Aufruf überspringen will und nur den Standard-Wert des dritten überschreiben will reicht es nicht aus einfach zwei Kommas anzugeben, sondern dann muss auf benannte Parameter zugegriffen werden. Der Aufruf mit dem ersten und dritten Parameter sieht dann folgendermaßen aus:

 

anExample.ExampleMethod(3, optionalint: 4);

 

Gemäß Link mus man bei Visual Basic.NET das Stichwort Optional davor schreiben. In der MSDN steht das optionale Parameter auch mit der .net-Klasse OptionalAttribute deklariert werden können, weitere Links dazu: 1 2

 

Allgemein wird dazu geraten, optionale Parameter nur für COM-Interoperabilität zu verwenden. Ich würde ansonsten mehrere überladene Methoden definieren und die grundlegende Logik in eine weitere (interne) Methode auslagern, die von diesen aufgerufen wird - das erscheint mir am elegantesten.

 

2. Dynamische Datentypen

 

sind Datentypen die beliebige Werte annehmen können, mit dem Schlüsselwort dynamic. In der MSDN gibt es dazu auch wieder einen eigenen Artikel: Link. Ein Beispiel sieht demnach so aus:

 

dynamic number = 10;
Console.WriteLine(number);

 

Von Herrn Roden wurden dynamische Datentypen einmal als "syntaktischer Zucker" bezeichnet, da intern mit object und Reflection (MSDN-Link) gearbeitet wird.

 

In der MSDN wird explizit darauf hingewiesen, das dynamic ein eigener Datentyp ist. Der Datentyp erinnert mich an alte Visual Basic 6-Zeiten, wo die mangelnde Typ-Sicherheit dieser Sprache überall bemängelt wurde, dazu gab es damals die Option Explicit-Direktive damit die Datentypen der Variablen angegeben werden _mussten_... hier ist dynamic zwar ein eigener Datentyp, aber er kann eben auch beliebige Werte annehmen.

 

Nachteil ist auch wie gesagt das syntaktische Fehler nicht mehr während des Kompilierens automatisch überprüft werden (z. b. ob eine Funktion SUM überhaupt in dem gewrappten Datentypen vorhanden ist), sondern erst zur Laufzeit des Programms festgestellt werden. Es wird jedenfalls allgemein davon abgeraten dieses Sprachfeature zu benutzen, es ist ebenfalls nur zur besseren Interoperabilität erschaffen worden....

 

3. Ko- und Kontravarianz von generischen Datentypen

 

In der aktuellen dotnetpro-Zeitschrift 09/2010 gibt es dazu auch einen Artikel, dabei hatte ich ein Déjá-vu weil ich mich mit dem Thema schon einmal befasst hatte. In dem dazugehörigen Wikipedia-Artikel gibt es folgende Definition:

 

In der objektorientierten Programmierung bedeutet Kovarianz und Kontravarianz, ob ein Aspekt gleichartig der Vererbungsrichtung (kovariant) oder entgegengesetzt zu dieser (kontravariant) ist. Liegt in der Unterklasse keine Änderung gegenüber der Oberklasse vor, wird das als Invarianz bezeichnet.

 

Die Grafik dort ist auch interessant, demnach bezieht sich die Kovarianz in C# auf den Rückgabewert einer Methode (und einer Ableitung der vorgegebenen Klasse) und die Kontravarianz auf die Parameter einer Methode (und einer übergeordneten Klasse).

 

Dazu habe ich im myCSharp-Forum folgende Definition inkl. Beispielen gefunden:

 

Kovarianz und Kontravarianz erlauben es, dass die Signatur des Delegattypen und die der Methode nicht exakt übereinstimmen müssen. Durch Kovarianz kann einem Delegat eine Methode zugewiesen werden, dessen Rückgabetyp eine Spezialisierung des Rückgabetyps des Delegaten ist. Hat der Delegat beispielsweise einen Rückgabewert vom Typ Stream, kann ihm auch eine Methode zugewiesen werden, die den Rückgabetyp FileStream hat.

 

Kontravarianz ermöglicht es dagegen, dass die Parametertypen der Methode eine Generalisierung des entsprechenden Parametertypen aus der Delegatdefinition ist. Hat beispielsweise der Delegat einen Parameter vom Typ FileStream, kann ihm eine Methode zugewiesen werden, dessen entsprechender Parameter den Typ Stream hat.

 

Signatur ist eine elegantere Bezeichnung für eine Deklaration. Jedenfalls, in C# 4.0 wurde dieses Feature auf generische Datentypen erweitert, im oreillyblog steht folgendes Beispiel für ältere C#-Versionen: 



List<string> ls = new List<string>();
List<object> lo = ls; // Kompilierte nicht, da dann
lo.Add(1);            // dies zulässig wäre.

 

Und hier ein Beispiel für C# 4.0, wo das zulässig ist: 

 


List<string> ls = new List<string>();
IEnumerable<object> ieo = ls;

 

Ergänzung 19.08.2010:  die Zuweisung funktioniert deswegen weil List<> von IEnumerable<> ableitet (Vererbungsrichtung nach unten, also Kovarianz) - in dem Beispiel trifft das auch auf den übergebenen Typ (Eingabeparameter) <string>, zu, welcher seinerseits von <object> ableitet. In der Wikipedia steht dazu übrigens

 

In der Objektorientierten Modellierung ist es oft wünschenswert, dass auch die Eingabeparameter von Methoden kovariant sind. Dadurch wird allerdings das Substitutionsprinzip verletzt. Das Überladen wird in diesem Fall von den verschiedenen Programmiersprachen unterschiedlich gehandhabt.

 

Wegen des Beispiels ein Link zum IEnumerable-Interface im MSDN, dieser Schnittstellen-Typ wird hier auch gut auf deutsch erklärt. Früher hat man dafür das Iterator-Design Pattern von Hand implementiert, heute muss man nur vom Interface IEnumerable ableiten und die Methode GetEnumerator() implementieren.

 

Ergänzung 10.11.2011: Zum Vergleich, Ko- und Kontravarianz in Java.

 

Weitere Links

 

 

Monats-Liste