Archive for Februar 2008

h1

.NET 3.5 – Neuerungen bei C#3.0 – Teil 7 von 9

Februar 11, 2008

LINQ

LINQ … Language INtegrated Query stellt eine Syntax für Abfragen zur Verfügung, die ähnlich der Syntax für relationale und hierarchische Datenabfragen wie SQL und XQuery ist. Die Abfrage beginnt dabei immer mit dem from Ausdruck und endet entweder mit einem select oder group by Ausdruck.

from c in customers
group c.Name by c.Country

Mit diesen Zeile wird aus der Liste der Customers eine nach Ländern sortierte Liste der Customer-Namen erzeugt. Das gleiche kann man auch mit Hilfe der bereits bekannten Extension-Methods und Lambda Expressions schreiben:

customers.GroupBy(c => c.Country, c => c.Name)

Es wird bei genauer Betrachtung schnell deutlich, dass bei LINQ die in den anderen Teilen besprochenen Neuerungen in .NET 3.5 ihre Anwendung finden. Es sind zum einen die Anonymen Typen notwendig, denn das Ergebnis der LINQ-Abfrage ergibt in der Regel einen neuen anonymen Typ. Weiterhin die Extension Methods, denn der Compiler macht aus der LINQ-Abfrage einen Funktionsaufruf, bei dem wiederum die Lambda-Ausdrücke wiederzufinden sind.

from c in customers  
where c.Name=="Microsoft" 
select c

entspricht:

customers.Where(c => c.Name == "Microsoft");

etwas komplexer:

from c in customers  
where c.Name=="Microsoft" 
select c.Country

entspricht:

customers.
Where(c => c.Name=="Microsoft").
Select(c => c.Country);

oder noch etwas komplexer:

from c in customers  
orderby c.Name, c.Balance descending 
select new {c.Country, c.Name, c.Balance}

entspricht:

customers.
OrderBy(c => c.Name).
ThenByDescending(c => c.Balance).
Select(c => new { c.Name, c.Country, c.Balance })

Weiterhin können auch mehrfache Abfragen kombiniert und verschachtelt werden:

from c in customers 
where c.City == "Dresden" 
from o in c.Orders
where o.OrderDate.Year == 2008
select new { c.Name, o.OrderID, o.Total }

entspricht:

customers.
Where(c => c.City == "Dresden"). 
SelectMany(c =>c.Orders.
Where(o => o.OrderDate.Year == 2008).
Select(o => new { c.Name, o.OrderID, o.Total })

Und noch ein Beispiel mit „into“:

from c in customers  
group c by c.Country  
into g 
select new { Country = g.Key, CustCount = g.Group.Count() }

kann auch so geschrieben werden:

from g in         
    from c in customers     
    group c by c.Country
select new { Country = g.Key, CustCount = g.Group.Count() }

entspricht:

customers. 
GroupBy(c => c.Country). 
Select(g => new { Country = g.Key, CustCount = g.Group.Count()})

Alles in allem ist LINQ wohl die komplexeste Erweiterung in .NET 3.5, da diese auch die meisten der besprochenen in sich vereint. Ich konnte hier also nur ganz kurz und grob auf die Syntax eingehen um einen ersten Start zu dem Thema zu finden. Es ist sicher sinnvoll sich mit LINQ to SQL (Thema O/RM) und mit den verfügbaren Beispielen und HoLs. Weiterhin bieten eine Reihe von Blogs eine Menge an Informationen wie z.B. dieser.

^ Teil 1: Übersicht aller Neuerungen (Einleitung)

< Teil 6: Anonymous Types

> Teil 8: Was geht mit Silverlight

h1

.NET 3.5 – Neuerungen bei C#3.0 – Teil 6 von 9

Februar 11, 2008

Anonymous Types

Ich denke, dass es sich bei den Anonymous Types um ein recht einfach verständliches Feature handelt. Vielleicht wird es als Einzel-Feature nicht unbedingt häufig Anwendung finden, aber spätestens mit LINQ wird auch dieses Feature im Gesamtkonzept eine Daseinsberechtigung haben. Folgendes Codebeispiel würde ich auch zu diesem Context zählen:

class Customer
{
    public string Name { get; set; }
    public int Age { get; set; }        
}

Auf den ersten Blick erscheint das wie eine abstrakte Klasse oder ein Interface, aber es handelt sich um ein .NET 3.5 Feature. Der Compiler generiert einfach die notwendigen privaten Variablen und macht aus diesen Zeilen Code jeweils einen Standard „getter“ und „setter“. Es spart dem faulen Programmierer einfach ein paar Zeilen Code. Auch folgende erweiterte Variante ist möglich:

class Customer
{
    public string Name { get; protected set; }
    public int Age { private get; set; }        
} 

Der Compiler ist auch hier Klever und generiert den entsprechenden Code, wobei in diesem Beispiel der „getter“ und „setter“ jeweils unterschiedliche Access-Modifier aufweisen.

Das folgende Beispiel zeigt wohl die in Zusammenhang mit LINQ häufigere Anwendung(Schreibweise) der anonymen Typen:

var p1 = new { Name = "Anonym", Price = 666.00 };
p1.Name = "Doch nicht Anonym";

Hier wird vom Compiler ein anonymer Typ mit zwei Properties generiert und auf die Variable p1 zugewiesen. Das schöne daran ist auch die vollständige Intellisense Unterstützung von Visual Studio 2008. Sogar folgende Zeilen werden vom Compiler ohne Fehler compiliert:

var p1 = new { Name = „Test1„, Price = 666.00 };
var p2 = new { Name = „Test2„, Price = 777.00 };
p1 = p2;

Hier wird vom Compiler ein und der selbe anonymer Typ für p1 und p2  generiert und somit ist auch die Zuweisung gültig. Das sind eigentlich die wichtigsten Fakten zu diesem Thema, was im nächsten Teil mit LINQ noch extensiver zum Einsatz kommt.

^ Teil 1: Übersicht aller Neuerungen (Einleitung)

< Teil 5: Object und Collection Initializer

> Teil 7: LINQ

h1

.NET 3.5 – Neuerungen bei C#3.0 – Teil 5 von 9

Februar 8, 2008

Object und Collection Initializer

Das Initialisieren von Objekten und Collections ist mit C#3.0 durch eine zusätzliche Möglichkeit bereichert worden. In erster Linie spart man meiner Meinung nach ein paar Zeilen Code und damit natürlich Zeit. Auf dem zweiten Blick erkennt man dann natürlich noch die Notwendigkeit dieser Möglichkeit im Gesamtkonzept von C#3.0. Das wird vielleicht erst ganz am Ende bei Betrachtung aller Neuigkeiten im Gesamtkonzept klar. Ein paar Beispiele erleichtern natürlich die Verständlichkeit. Also hier die neuen Object Initializer:

var c = new Customer {Id = 1, Name = "Microsoft"};

Was der bisherigen Schreibweise entspricht:

Customer c = new Customer();
c.Id = 1;
c.Name = "Microsoft";

Das ganze kann natürlich auch in verschachtelter Form angewendet werden. Im folgenden Beispiel wird eine Linie mit zwei Punkten und deren Koordinaten initialisiert:

var l = new Line{
 P1 = {X = 0, Y = 0},
 P2 = {X = 10, Y = 10}
};

Das entspricht:

Line l = new Line();
l.P1.X = 0;
l.P1.Y = 0;
l.P2.X = 10;
l.P2.Y = 10;

Viel mehr kann man dazu eigentlich nicht erzählen. Also noch was zu den Collection Initializern:

List<int> l = new List<int> {0, 1, 2, 3};

und ein bisschen komplexer:

var c = new List<Customer>{
    new Customer {Name = "Microsoft",
        PhoneNumbers = { "206-555-0101", "425-882-8080" }},
    new Customer {Name = "Basta",
        PhoneNumbers = { "206-666-0101", "425-888-8080" }}
};

was man auch wie folgt schreiben könnte:

var c = new List<Customer>();
var _c1 = new Customer();
_c1.Name = "Microsoft";
_c1.PhoneNumbers.Add("206-555-0101");
_c1.PhoneNumbers.Add("425-882-8080");
c.Add(_c1);
var _c2 = new Customer();
_c2.Name = "Basta";
_c2.PhoneNumbers.Add("206-666-0101");
_c2.PhoneNumbers.Add("425-888-8080");
c.Add(_c2);

Es ist denke ich eines der leichter verständlichen und am schnellsten anwendbaren neuen Features von .NET 3.5
Viel Spaß beim verwenden…

^ Teil 1: Übersicht aller Neuerungen (Einleitung)

< Teil 4: Lambda Expressions

> Teil 6: Anonymous Types

h1

.NET 3.5 – Neuerungen bei C#3.0 – Teil 4 von 9

Februar 8, 2008

Expression Trees und Lambda Expressions

Nach langer langer Zeit will ich mal wieder in meiner kleinen Serie fortfahren. Da es jetzt schon etwas komplexer wird habe ich mich so lange gesträubt hier weiter zu schreiben. Jetzt versuche ich mal wieder so einfach wie möglich in das Thema einzuführen.
Eigentlich liegt der Ursprung in den anonymen Methoden, die mit C# 2.0 eingeführt wurden. Zur Erinnerung ein kurzes Beispiel einer anonymen Methode:

IEnummerable<Customer> list = customers.Where(
	delegate(Customer c)
	{
		return c.Name == "Microsoft";
	});

In diesem Beispiel ist das es das Ziel alle „customers“ mit dem Namen „Microsoft“ in einer Enummeration zusammenzufassen. Hier ist auch das erste mal ersichtlich, dass wir mit der Methode „Where“ auf ein bereits erläutertes neues Feature der Extension Methods zurückgreifen.

public static IEnumerable<T> Where<T>(
	this IEnumerable<T>   source, Func<T, bool> predicate)
{
	foreach(T item in source) {
		if (predicate(item)) yield return item;
	}
}

Mit C#3.0 kann man nun die oben gezeigte anonyme Methode auf eine Zeile Code reduzieren:

IEnummerable<Customer> list = 
	customers.Where(c=>c.Name=="Microsoft");

In C# wird ein Lambda-Ausdruck syntaktisch als Parameter-Liste gefolgt von einem => Token und dann gefolgt von dem Ausdruck oder einer ausführ-baren Block-Anweisung (wenn der Ausdruck aufgerufen wird) geschrieben:

params => Ausdruck

also in diesem Beispiel:

c => c.Name == „Microsoft“ oder in ausführlicher Schreibweise
(Customer c) => c.Name == „Microsoft“

x => x + 1     // Implicitly typed, expression body 
x => {return x + 1;}// Implicitly typed, statement body 
(int x) => x + 1    // Explicitly typed, expression body 
(int x) => {return x + 1;} // Explicitly typed, stmnt body 
(x, y) => x * y            // Multiple parameters 
() => Console.WriteLine()  // No parameters 

Im Gegensatz zu anonymen Methoden muss bei Lambda Expression der verwendete Typ nicht explizit angegeben werden. Bei der Schreibweise c=>c.Name ist dem Compiler bereits klar, dass c vom Typ Customer sein muss, da die Extension Methode „Where“ mit einer generischen Liste vom Typ „Customer“ gearbeitet hat. Bei Verwendung von Visual Studio 2008 handelt es sich nicht nur um Compilezeit Support, sondern es wird auch bei der Designzeit mit Intellisense unterstützt.

Einige Dinge müssen beachtet werden:

delegate R Func<A,R>(A arg);

–>

Func<int,int> f1 = x => x + 1; // Ok 
Func<int,double> f2 = x => x + 1; // Ok 
Func<double,int> f3 = x => x + 1; // Error 

In der letzten Zeile ist „x+1“ nicht implizit ein double, weswegen diese Zeile schon beim Compilieren zum Fehler führt.

Was sind nun Expression Trees?

Eines der Dinge warum die Lambda-Ausdrücke so ein mächtiges Werkzeug aus der Entwickler-Perspektive sind, ist dass man sie kompiliert kann. Entweder als delegate-Code (in Form einer IL Methode) oder als Expression Tree Objekt, die benutzt während der Laufzeit benutzt werden können um den Ausdruck zum optimieren, zu analysieren oder zu transformieren. Die Extension Methode „Where“ im oberen Beispiel zeigt wie man diese in Form eines delegate Code kompilieren kann.

Kompilierte Lambda-Ausdrücke funktioniert toll, wenn wir diese für Daten innerhalb des Speichers verwenden wollen. Aber bei Fällen, in denen Sie Abfrage von Daten aus einer Datenbank durchführen wollen, ist das nicht immer der geeignete Weg. Stattdessen würde ich gerne das LINQ zu SQL ORM zur Übersetzung meines Lambda-Filters in einen SQL-Ausdruck verwenden und die Filter-Abfrage in der Remote-SQL-Datenbank durchführen lassen. Auf diese Weise würde ich nur die gefilterten Zeilen als Ergebnis erhalten.

Func<int,int> f = x => x + 1; // Code 
Expression<Func<int,int>> e = x => x + 1; // Data 

Framework-Entwickler können dies erreichen, indem sie die Argumente zu Lambda-Ausdruck vom Typ Expression <T> statt Func <T> erklären. Dies führt dazu, dass ein Lambda-Ausdruck Argument als Expression-Tree kompiliert wird. Dieser kann dann zur Laufzeit analysiert werden:

Expression<Func>Customer, bool>> filter;
filter = c => c.Name = "Microsoft";
BinaryExpression body = (BinaryExpression)filter.Body;

Im Fall von LINQ zu SQL, kann dieser Expression und Lambda-Filter verwendet werden, um diesen in SQL-Standard zu übersetzen und gegen eine Datenbank auszuführen (logisch „SELECT * from Customers where Name = ‚Microsoft'“).

^ Teil 1: Übersicht aller Neuerungen (Einleitung)

< Teil 3: Extension-Methods

> Teil 5: Object und Collection Initializer