Neuerungen in C# 3.0
C# in der Version 3.0 (auch bekannt als „Orcas“) brachte einige neue Spracherweiterungen mit, die das Arbeiten deutlich erleichtern. Viele der neuen Techniken erlauben eine höhere Abstraktion des Codes, was die Wiederverwendbarkeit steigert – ohne dabei die Typsicherheit zu opfern.
Typinferenz
Bislang musste beim Deklarieren einer Variable immer der passende Typ angegeben werden:
int n = 5; string x = "Hallo";
Mit dem Schlüsselwort var lässt sich das abkürzen:
var n = 5; var x = "Hallo";
Anders als beim COM-Variant-Datentyp bleibt die Typsicherheit dabei vollständig erhalten – der Typ wird beim Kompilieren ermittelt, nicht zur Laufzeit:
int x = 5; var a = 4; var b = "hallo"; int c = x + a; // funktioniert int d = x + b; // funktioniert nicht – b ist vom Typ string
Diese „implizit typisierte lokale Variablendeklaration“ sollte jedoch nicht die einzige
Deklarationsart werden: Sie verringert die Lesbarkeit, da der Datentyp nicht mehr auf den ersten
Blick erkennbar ist. Zudem ist var nur für lokale Variablen erlaubt – nicht für
Rückgabewerte, Parameter oder Klassenmember – und darf nicht mit null initialisiert werden.
Lambda-Ausdrücke
Lambda-Ausdrücke sind vergleichbar mit Funktionszeigern in C# 2.0:
List<string> myNames = new List<string>();
myNames.Add("Marcel");
myNames.Add("Sarah");
myNames.Add("Tobi");
myNames.Add("Andi");
// Alte Syntax
List<string> filtered1 = myNames.FindAll(FindMethod);
private static bool FindMethod(string s)
{
return (s.Length == 6);
}
Mit Lambda-Ausdrücken lässt sich dasselbe deutlich kompakter formulieren:
List<string> filtered2 = myNames.FindAll((s) => s.Length == 6);
Der Ausdruck im Detail:
(s)– der Parameter, der als Delegate-Parameter inFindAllhinterlegt ist (mehrere Parameter werden per Komma getrennt)=>– der Lambda-Operator: rechts folgt die aufzurufende Funktions.Length == 6– der eigentliche Funktionsinhalt (bei einer einzelnen Anweisung kannreturnentfallen)
Erweiterungsmethoden
Mit Erweiterungsmethoden lassen sich Klassen ohne Ableitung um Funktionen erweitern:
public static class MyExtensions
{
public static int AddNumber(this int n, int number)
{
return (n + number);
}
}
Im Code kann die Methode sofort verwendet werden:
int myNumber = 5; int y = myNumber.AddNumber(40); // y == 45
Eine Erweiterungsmethode muss in einer öffentlichen, statischen Klasse liegen und selbst öffentlich
und statisch sein. Das Schlüsselwort this vor dem ersten Parameter markiert den
erweiterten Datentyp – auch mit Generics:
public static class MyExtensions
{
public static void WriteType<T>(this T obj)
{
System.Diagnostics.Debug.WriteLine(obj.ToString());
}
}
Gerade LINQ macht von Erweiterungsmethoden exzessiv Gebrauch, um die Lesbarkeit von Queries zu erhöhen.
Initialisierung von Objekten
Bislang mussten Eigenschaften nach der Objekterstellung einzeln gesetzt werden:
Order myOrder = new Order(); myOrder.OrderID = 4; myOrder.OrderInfo = "Test";
Ab C# 3.0 lassen sich öffentliche Eigenschaften direkt bei der Initialisierung setzen:
Order myOrder = new Order { OrderID = 4, OrderInfo = "Test" };
// auch zusammen mit einem Konstruktor:
Order myOrder1 = new Order(5) { OrderInfo = "Test" };
// und verschachtelt:
Order myOrder2 = new Order(5) { OrderInfo = "Test", Details = new OrderDetails { ID = 1, Product = "AFKI" } };
Anonyme Typen
Mit anonymen Typen muss der Zieltyp nicht mehr explizit angegeben werden:
var myNewType = new { OrderID = 4, OrderInfo = "Test" };
myNewType.OrderID = 5; // sofort mit IntelliSense nutzbar
Der Compiler erzeugt für jedes Argument eine öffentliche Eigenschaft und ein privates Feld. Typen mit gleichem Namen und gleicher Reihenfolge der Argumente teilen sich denselben anonymen Typ:
var myNewType = new { OrderID = 4, OrderInfo = "Test", this.Text }; // Type: <>f__AnonymousType0`3
var myNewType2 = new { OrderID = 5, OrderInfo = "Test", this.Text }; // Type: <>f__AnonymousType0`3
var myNewType3 = new { OrderInfo = "Test", OrderID = 5, this.Text }; // Type: <>f__AnonymousType1`3
Bei LINQ-Abfragen spielen anonyme Typen eine zentrale Rolle.