Sebastians Weblog

ESCde Developer Blog

  Home :: Kontakt :: RSS Feed
  3 Posts :: 0 Artikel :: 7 Kommentare :: 17 Trackbacks

Archiv

Post Kategorien

ESCde

ESCde Blogger

Unter folgendem Link finden Sie diesen Blog Eintrag als Word Dokument: CSharp3Whitepaper

Einleitung

 

Dieses Whitepaper beschäftigt sich mit den Erweiterungen und Veränderungen von C# 3.0. C# 3.0 wird mit dem .NET Framework 3.5 (zusammen mit Visual Studio Codename Orcas) voraussichtlich Mitte 2007 erscheinen. Beachten Sie, dass C# 3.0 nicht Teil des .NET Framework 3.0 ist. Es mag von den Versionsnummern her etwas verwirrend sein, aber das .NET Framework 3.0 enthält weder Compilerupdates noch eine veränderte CLR, sondern nur neue Bibliotheken und wurde aus rein Marktstrategischen Gründen seitens Microsoft von WinFX in .NET 3.0 umbenannt. Die Erweiterungen in C# 3.0 reichen von kleinen Veränderungen wie z.B. implizit typisierten lokalen Variablen bis hin zu ziemlich neuen Funktionen wie z.B. Erweiterungsmethoden. Sogar revolutionäre Neuerungen wie etwa die normalerweise aus der Datenbank -Welt bekannten Query Expressions sind in C# 3.0 zu finden (diese werden als LINQ bezeichnet).

 

Implizit typisierte lokale Variablen

 

In C# 3.0 kann der Datentyp lokaler Variablen bei der Deklaration vom Compiler automatisch, also ohne dass explizit der Typ angegeben wird, bestimmt werden. Dafür wird das neue Schlüsselwort var verwendet. Ein Beispiel:

 

var deepThought = 42;

 

Der Compiler wertet hier dann die Initialisierung der Variable deepThought aus, und legt aufgrund des Wertes 42 den Typ von deepThought auf Integer fest. Dies bedeutet, dass die obige Zeile semantisch identisch mit folgendem ist:

 

int deepThought = 42;

 

Der Datentyp wird einmalig bei der ersten Deklaration der Variablen vom Compiler festgelegt, und kann danach nicht mehr verändert werden:

 

var i = 5; // i wird vom Compiler als Integer festgelegt

 

i = 25.2;  //Fehler, da i Integer ist, und kein Double

 

Damit der Compiler den Typ der Variable feststellen kann, muss eine mit dem Schlüsselwort var deklarierte Variable unbedingt bei der Deklaration schon initialisiert werden, da aus der Intitialisierung der Datentyp der Variable vom Compiler abgeleitet wird. Daraus ergibt sich auch, dass eine Initialisierung mit null in diesem Zusammenhang nicht zulässig ist. Weitere Beispiele sind:

 

var fehler   = null;                 //Fehler -> ist nicht erlaubt

var fehler2;                         //Fehler -> Initialiserung fehlt

     

var liste    = new List<int>();      //Typ: List<int>

var text     = "Das ist ein Text";   //Typ : String

var pi       = 3.1415;               //Typ double

var arrayVar = new int[42];          //Typ: int[]

 

foreach (var myInt in arrayVar)      //Typ von myInt: int

{

Console.WriteLine(myInt);

}

 

Auch diese Beispiele sind natürlich wieder semantisch äquivalent zur „herkömmlichen“ Deklaration wo der Datentyp explizit angegeben wird.

Wie man in späteren Kapiteln dieses Whitepapers noch sehen wird, erweist sich das var Schlüsselwort vor allem für Anonyme Typen und Linq als notwendig bzw. praktisch.

Erweiterungsmethoden

 

Erweiterungsmethoden stellen eine interessante neue Möglichkeit dar, um Klassen oder auch Strukturen zu ergänzen. Dies umfasst auch Klassen und Strukturen, bei denen es bisher nicht möglich war, so z.B. Klassen die sealed sind oder Strukturen wie System.Int32. Erweiterungsmethoden werden als statische Methode in einer neuen static Klasse implementiert, und können dann wie eine normale Methode (d.h. Instanzmethode) des erweiterten Datentyps aufgerufen werden. Um eine Methode als Erweiterungsmethode zu deklarieren, wird vor dem ersten Parameter das Schlüsselwort this angegeben. Ferner gibt der Argumenttyp des ersten Parameters die zu erweiternde Klasse bzw. Struktur an. Wenn die Erweiterungsmethode dann aufgerufen wird,  übergibt der Compiler die Instanz des erweiterten Typs als erstes Argument an die Methode. Erweiterungsmethoden können z.B. so aussehen:

 

public static class Extension

{    

//Erweiterungsmethode für Int

public static int Betrag(this int instanz)

      {

            if (instanz < 0)

            {

                return -1 * instanz;

            }

 

            return instanz;

      }

 

// 2. Erweiterungsmethode, auch für Int

      public static int Faktor(this int instanz, int faktor)

      {

            return instanz * faktor;

      }

}

 

int testZahl = -42;

 

Console.WriteLine(testZahl.Betrag().ToString());     // Ausgabe: 42

 

Console.WriteLine(testZahl.Faktor(5).ToString());    // Ausgabe: -210

 

 

Das Beispiel gibt 42 und – 210 aus. In diesem Beispiel kann man nun die Betrag und Faktor Methoden für jede Integer Variable nutzen, als wären die Methoden direkt in der Basisklasse System.Int32 als Instanzmethoden vorhanden. Wenn in der System.Int32 Struktur bereits eine Betrag-Methode mit der gleichen Signatur wie die gleichnamige Erweiterungsmethode existieren würde, so hätte die bereits vorhandene Methode in System.Int32 Vorrang vor der Erweiterungsmethode. Zudem muss auch die Einordnung der Erweiterungsmethode bezüglich des Namensraumes beachtet werden, da der Compiler die erste Erweiterungsmethode nimmt die (hinsichtlich der Namensräume) erreichbar ist.

 

 

Ein weiterer interessanter Aspekt ist, dass Erweiterungsmethoden auch vererbt werden, wie im folgenden Beispiel sichtbar ist:

 

 

public class BaseClass { }

 

public class InheritedClass : BaseClass { }

 

public static class Extension

{    

//erweitert BaseClass

public static string WriteType(this BaseClass b)

      {

            return b.GetType().ToString();

      }

}

 

 

InheritedClass inhClass = new InheritedClass();

 

Console.WriteLine(inhClass.WriteType());  //gibt "Test.InheritedClass" aus

 

 

Die Erweiterungsmethode WriteType erweitert eigentlich die Klasse BaseClass. Man kann aber WriteType nun auch als Instanzmethode aller Klassen nutzen, die von BaseClass abgeleitet sind. In dem Beispiel wird WriteType also wie eine Instanzmethode von InheritedClass behandelt, da InheritedClass von BaseClass abgeleitet ist.

Objekt Initialisierer

 

Durch Objekt Initialisierer wird es in C# 3.0 möglich, ähnlich der Initialisierung von Attributen elegant Felder und Eigenschaften einer Klasse oder Struktur zu initialisieren. Somit können nun öffentliche Eigenschaften und Felder von Objekten ohne das explizite Vorhandensein des jeweiligen Konstruktors in beliebiger Reihenfolge initialisiert werden. Dies geschieht über geschweifte Klammern, in denen die einzelnen Felder oder Eigenschaften des Objektes mit Werten belegt werden:

 

 

public class Adresse

{

public string Name;

      public string Strasse;

      public int PLZ;

      public string Ort;

}

 

Adresse adrEsc = new Adresse { Name = "ESC Deutschland", Strasse =

 "Am Fasanengarten 5", PLZ = 76131,

 Ort = "Karlsruhe" };

 

Ein weiteres Beispiel mit Schachtelung:

 

Rectangle rect = new Rectangle { Location = new Point { X = 7, Y = 3 },

    Size = new Size { Width = 21,

    Height = 42 }

  };

 

 

Im Beispiel oben hätte man das gleiche einfach über entsprechend verfügbare Konstruktoren erreichen können, aber häufig sind solche umfangreichen Konstruktoren einfach nicht vorhanden. Diese Art von Konstruktoren sind in C# 3.0 nun aber auch nicht mehr notwendig.

Collection Initialisierer

 

Mithilfe von Collection Initialisierern kann man bei der Initialisierung eines Objektes, das ICollection<T> (z.B. List<T>) implementiert, elegant Elemente hinzufügen. Man verwendet geschweifte Klammern, in denen die einzelnen Elemente mit Kommata voneinander getrennt sind – was dann zum Beispiel so aussieht:

 

List<int> intList = new List<int>() { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };

 

Für die angegebenen Elemente wird entsprechend ihrer Reihenfolge die Methode

ICollection<T>.Add(T element)  aufgerufen. Natürlich müssen die aufgelisteten Elemente vom Typ T sein oder es muss eine implizite Konvertierung zu T existieren, wie in den folgenden zwei Beispielen deutlich wird:

 

 

//Fehler, double kann nicht (implizit) in int konvertiert werden

List<int> intList = new List<int>() { 0, 1, 1, 2, 3.58};

 

//OK, da implizite Konvertierungen von float zu double und von int zu //double existieren

List<double> intList = new List<double>() { 0, 1, 1, 2, 3F};

Anonyme Typen

 

Anonyme Typen sind vom Compiler automatisch erzeugte simple, namenlose Klassen, die nur über readonly Eigenschaften (also nur mit get) und dazugehörige private Felder verfügen. Mit namenlos ist gemeint, dass der Compiler der Klasse einen uns nicht bekannten Namen gibt, so dass man nicht direkt Zugriff auf die Klasse hat. Stattdessen erhält man lediglich eine Instanz die man ausschließlich lokal (im Bereich der Deklaration) verwenden kann.

Anonyme Typen werden mittels eines anonymen Objekt Initialisierers deklariert, also ein Objekt Initialisierer bei dem man den Klassennamen weglässt. Durch diesen anonymen Objekt Initialisierer wird dann die Klasse mit den entsprechenden Eigenschaften erzeugt und man erhält eine Instanz. Anhand der Eigenschaften im Objekt Initialisierer und des jeweiligen Typs der zugewiesenen Werte erzeugt der Compiler die anonyme Klasse, was dann etwa wie folgt aussehen kann:

 

 

var person = new { Vorname = "Bill", Nachname = "Gates", Alter = 56};

 

Hierfür generiert der Compiler intern (in MSIL) die folgende Klasse:

 

    internal class ??????

    {

        private string _vorname;

        private string _nachname;

        private int _alter;

 

        public string Vorname

        {

            get { return _vorname; }

        }

 

 

        public string Nachname

        {

            get { return _nachname; }

        }

 

 

        public int Alter

        {

            get { return _alter; }

        }

    }

 

 

Die vom Compiler generierte Klasse enthält wirklich nur readonly Eigenschaften und entsprechende  Felder, wie auch oben im Beispiel dargestellt.

Da der Typ der Eigenschaften aus der jeweiligen Klasse bzw. Struktur des zugewiesenen Wertes im Objekt Initialisierer abgeleitet wird, darf man hier nicht null zuweisen – ansonsten  kann der Compiler den Datentyp der Eigenschaft nicht bestimmen:

 

 

//Fehler -> Compiler kann Typ von Eigenschaft „Taktfrequenz“ nicht ableiten

var pc = new { CPU = "Intel P4" , Taktfrequenz = null, Ram = 2048};

 

 

Sobald eine weitere anonyme Klasse deklariert wird, bei der im Objekt Initialisierer Eigenschaften mit dem gleichen Namen, Typ und in der gleichen Reihenfolge wie

bei einer anderen bereits vorhandenen anonymen Klassen angegeben sind, verwendet der Compiler die gleiche anonyme Klasse und es sind untereinander Zuweisungen möglich:

 

 

var pc = new { CPU = "Intel P4" , Cores = 1,

   Taktfrequenz = 2.66, Ram = 2048};

 

var schnellererPC = new { CPU = "Intel Core 2 Duo Extreme", Cores = 2,

   Taktfrequenz = 6.00, Ram = 4096};

 

pc = schnellererPC;

 

 

Wie man oben sehen kann, sind der Name, der Typ und die Reihenfolge der Eigenschaften im Objekt Initialisierer bei pc und schnellererPC identisch, und somit ist schnellererPC vom gleichen Typ wie pc. Diese Tatsache erlaubt es uns in der 3. Zeile des Beispiels dass wir die beiden Variablen untereinander zuweisen können.

 

 

Lambda Expressions

 

Lambda Expressions sind funktional erweiterte anonyme Methoden (anonyme Methoden gibt es seit C# 2.0). Der augenscheinlichste Unterschied zu anonymen Methoden ist die Syntax: auf die Parameterliste folgt ein Doppelpfeil (=>), gefolgt von einer einzelnen Anweisung oder einem Anweisungsblock, z.B.:

 

(int x, int y) => x + y  //nur eine Anweisung

 

oder:

 

//mit Anweisungsblock

(int x) => { if( x < 0) 

                  x = -1 * x;

     

             return x;

           }

 

 

Der Typ des Ausdruckes hinter dem Doppelpfeil bestimmt den Rückgabetyp der Lambda Expression. Dementsprechend hat eine Lambda Expression mit nur einem Methodenaufruf nach dem Doppelpfeil den gleichen Rückgabetyp wie diese Methode. Falls dem Doppelpfeil ein Anweisungsblock folgt, so wird der Rückgabetyp durch den Ausdruck nach dem return Schlüsselwort festgelegt. Sollte das return Schlüsselwort im Anweisungsblock fehlen, dann ist der Rückgabetyp der Lambda Expression void. In folgenden Beispielen wird die Bestimmung des Rückgabetyps veranschaulicht:

 

 

//Rückgabetyp ist void, da Console.WriteLine() void als Rückgabetyp hat

(int i) => Console.WriteLine(i.ToString());

 

 

//Rückgabetyp ist Int, da x und y vom Typ Int sind

(int x, int y) => x + y; 

 

 

//Rückgabetyp ist void, da kein return Schlüsselwort im Block nach //Doppelpfeil

(double x) => { x = 3.14;

                MessageBox.Show(x.ToString());}

 

 

//Rückgabetyp ist double, da ein double (0.5) zu einem

//int addiert wird und das Ergebnis ein double ist

(int i) => i + 0.5;

 

 

//Rückgabetyp ist string, da nach Doppelpfeil

//ein string zusammengesetzt wird

(int alter) => "Glückwunsch zum " + alter + ". !"

 

 

Genau wie bei anonymen Methoden auch muss natürlich die Anzahl der Parameter und deren jeweiliger Datentyp mit denen des Delegate, für den die Lambda Expression angegeben wird, übereinstimmen. Allerdings kann bei Lambda Expressions auf die Angabe des Typs für die Parameter verzichtet werden, da diese vom Kontext her abgeleitet werden können. Dem jeweiligen Parameter der Lambda Expression wird also automatisch der Typ des entsprechenden Parameters des Delegate, für den die Lambda Expression angegeben wird, zugewiesen. In folgendem Beispiel kann man dieses Konzept sehen:

 

public class Person

{

public string Name = "";

public int Alter = 0;

}

 

public delegate void TestDelegate(Person person);

 

//wegen TestDelegate ist der Parameter x vom Typ Person

//Rückgabetyp muss void sein, auch durch TestDelegate vorgegeben

TestDelegate del = ( x ) => Console.WriteLine(x.Name);

 

//nun darf man bei nur einem Parameter auch die Klammern weglassen

TestDelegate del2 = x => Console.WriteLine(x.Name);

 

Gleichermaßen wie bei den Parametern muss natürlich auch der Rückgabetyp der Lambda Expression mit dem Rückgabetypen des Delegate übereinstimmen (bzw. eine implizite Konvertierung existieren). Demgemäß liefert die Lambda Expression im obigen Beispiel keinen Rückgabewert, da void vom Delegate TestDelegate entsprechend als „Rückgabewert“ vorgegeben wird. Ein weiteres Beispiel hierfür:

 

public delegate int AnotherTestDelegate();

 

AnotherTestDelegate del = () => 0.5; //Fehler

 

Dieses Beispiel produziert einen Compiler-Fehler, weil durch den Delegate AnotherTestDelegate ein Integer als Rückgabewert erwartet wird, die Lambda Expression aber einen double zurückgibt, der auch nicht implizit zu einem Integer konvertiert werden kann.  Man beachte die leeren Klammern vor dem Doppelpfeil in der Lambda Expression. Diese bedeuten, dass die Lambda Expression über keinerlei Eingabeparameter verfügt, gemäß der Definition des Delegate AnotherTestDelegate.

Im Gegensatz zu anonymen Methoden unterliegen Lambda Expressions auch den Regeln des Methoden - Überladens und des Ableitens von Parametertypen für Generics. Nehmen wir etwa folgendes Beispiel:

 

delegate double TestDel<T>(T a);

 

private static void DoSimpleMath(TestDel<int> testDel)

{

Console.WriteLine("int");

}

 

private static void DoSimpleMath(TestDel<double> testDel)

{

Console.WriteLine("double");

}

 

DoSimpleMath((double x) => x + 0.5);

 

 

Das obige Beispiel gibt „double“ aus, da der Datentyp des Parameters für die Lambda Expression double ist (siehe gelbe Markierung). Somit wird für den generischen  Platzhalter T von TestDel (dem Argument -Typ der beiden DoSimpleMath Methoden) double als Typ eingesetzt und damit dementsprechend die DoSimpleMath(TestDel<double> testDel) Methode aufgerufen. Gleichermaßen wird auch für den Rückgabetypen der Lambda Expressions der „passendste“ generische Delegate und damit die entsprechend passende überladene Methode aufgerufen:

 

delegate T TestDel<T>();

 

private static void DoSimpleMath(TestDel<int> testDel)

{

Console.WriteLine("int");

}

 

private static void DoSimpleMath(TestDel<double> testDel)

{

Console.WriteLine("double");

}

 

DoSimpleMath(() => 5);

 

 

Dieses Beispiel gibt nun „int“ aus, da die Lambda Expression einen int (siehe gelbe Markierung) zurückgibt und somit der (Rückgabe -) Platzhalter T vom Delegate TestDel auf int festgelegt wird. In Folge dessen wird die DoSimpleMath(TestDel<int> testDel) Methode verwendet. Für die Zeile

 

DoSimpleMath(() => 42.0);

 

wird analog entsprechend „double“ ausgegeben, also die DoSimpleMath(TestDel<double> testDel) Methode aufgerufen, da nun die Lambda Expression einen double zurückgibt (siehe gelbe Markierung).

LINQ

 

LINQ (Language Integrated Query) ist die größte Neuerung in C# 3.0. Wie der Name schon besagt, handelt es sich dabei um in die Sprache integrierte, SQL ähnliche Strings, mit denen fast jede Art von Daten vergleichsweise elegant gefiltert, gruppiert und sortiert werden kann. Nehmen wir folgendes einfaches Beispiel:

 

int[] integers = new int[] { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55};

 

IEnumerable<int> evenNumbers =

from myNumber in integers where myNumber % 2 == 0 select myNumber;

 

foreach (int number in evenNumbers)

{

Console.WriteLine(number.ToString());

}

 

Dieses Beispiel gibt 0, 2, 8 und 34 aus, also alle geraden Zahlen des Arrays integer. In der 2. Zeile des Beispiels kann man dann auch die eigentliche LINQ Expression sehen:

 

from myNumber in integers where myNumber % 2 == 0 select myNumber

 

Wie alle LINQ Ausdrücke in C# fängt auch dieser mit dem from  Schlüsselwort an. Es folgt eine Laufvariable (myNumber), die ähnlich funktioniert wie die Laufvariable in einer foreach Schleife:

 

foreach (int myNumber in integers)

{  }

 

Die Quellmenge des LINQ Ausdruckes (im Beispiel das Array integers) wird ähnlich einer Schleife „durchlaufen“ und jedes einzelne Elemente dann jeweils durch diese Laufvariable repräsentiert. Diese Laufvariable ist die eigentliche Variable auf die innerhalb des LINQ Ausdruckes sämtliche Operatoren (z.B. Filterkriterien) angewandt werden. Nach dem in Schlüsselwort kommt die zu filternde Menge (hier integers), gefolgt von dem where Operator mit dem eigentlichen Filterausdruck als Lambda Expression:

 

myNumber % 2 == 0  //wähle alle geraden Zahlen

 

Der where Operator hat die gleiche Funktion wie der gleichnamige Operator in SQL, sprich das Filtern. Dieser Filterausdruck wird nun auf jedes einzelne Element vom Array integers angewandt. Über den select Operator gibt man an, was genau vom Element zurückgegeben werden soll, wie im nächsten Beispiel besser sichtbar wird:

 

public class Person

{

public string Name = "";

public int Alter = 0;

}

 

 

 

 

Person[] persons = new Person[] {new Person {Name = "Arthur", Alter = 35},

    new Person { Name = "Marvin", Alter = 40},   

    new Person { Name = "Ford", Alter = 55} };

 

IEnumerable<string> filteredPersons =

from person in persons where person.Alter < 50 select person.Name;

 

 

foreach (string name in filteredPersons)

{    

//gibt die Namen der Personen mit Alter < 50 aus

Console.WriteLine(name);

}

 

In diesem Beispiel wird nun jeweils nur der Name der Personen, die dem Filterkriterium (Alter < 50) entsprechen, zurückgegeben – gemäß der Auswahl mit dem select Operator (person.Name).

In beiden Beispielen oben kann man sehen, dass der LINQ Ausdruck eine Instanz vom Typ IEnumerable<T> zurückgibt. Dies ist aber nicht immer so, da der Rückgabetyp von den benutzten LINQ -Operatoren abhängt.  So gibt es z.B. den Count Operator, der die Anzahl der Elemente, die zu einem Filterausdruck oder Ähnlichem passen, als Integer zurückgibt:

 

 

int[] integers = new int[] {0,1,3,5,7,9,12,42, 99, 200, 300 };

 

int count = (from number in integers where number < 42 select

number).Count();

 

Console.WriteLine(count.ToString()); //Ausgabe: 7

 

 

Hier wird nun mit Hilfe des Count Operators die Anzahl der Zahlen ausgegeben, die kleiner als 42 sind. Vielleicht fragen Sie sich jetzt, warum der Count Operator wie eine Methode aufgerufen wird. Das hängt damit zusammen, dass eigentlich alle LINQ Operatoren als Methoden implementiert sind. Genauer gesagt sind sie als Erweiterungsmethoden in der System.Query.Sequence Klasse enthalten und erweitern alle Typen die IEnumerable<T> implementiert haben - also unter anderem sämtliche Arrays, List<T> und Stack<T> sowie viele mehr. Daher können LINQ Expressions auf alle diese Typen angewandt werden. Praktischerweise kann man dadurch auch die komplexeren Klassen im Array wie Daten aus Datenbanken behandeln und entsprechend mit LINQ elegant filtern, sortieren und gruppieren usw. Hierfür gibt es dann auch dutzende Operatoren (eine kompakte Übersicht mit Beispielen finden Sie unter  http://msdn2.microsoft.com/en-us/vcsharp/aa336746.aspx), von denen einige auch von Datenbankabfrage -Sprachen wie SQL her bekannt sind und ähnlich funktionieren - so z.B. die Operatoren Count, Average und GroupBy. Da wie bereits beschrieben die Operatoren als Methoden realisiert sind, können sie auch ganz normal als solche entsprechend aufgerufen werden. Die Syntax, die Sie in den ersten beiden LINQ Beispielen gesehen haben, ist also nichts weiter als „Syntaktischer Zucker“ - der C# Compiler ruft einfach für die Schlüsselwörter from, where und select etc. die dazu passenden LINQ Methoden auf. Somit sind auch die beiden folgenden LINQ Ausdrücke semantisch äquivalent:

 

 

//Linq Ausdruck mit C# Schlüsselwörtern

IEnumerable<int> numbersSmallerThan42 = from number in integers where

number < 42 select number + 10;

 

 

//der gleiche Linq Ausdruck wie oben, aber durch Methodenaufrufe der //entsprechenden Linq -Operatoren (wird Punkt -Notation genannt)

IEnumerable<int> againNumbersSmallerThan42 = integers.Where(i =>

i < 42).Select(i => i + 10);

 

 

Das Beispiel gibt alle Zahlen aus dem Array integers zurück die kleiner 42 sind und addiert dann jeweils 10. Welche der beiden Schreibweisen (C# Schlüsselwörter oder Punkt -Notation) Sie bevorzugen und nutzen bleibt somit ganz Ihnen überlassen.

 

Ein weiterer wichtiger Aspekt bei LINQ ist der Zeitpunkt, zu dem der LINQ Ausdruck ausgeführt wird. Die LINQ Expression wird aufgrund ihrer internen Implementierung (mittels yield) nicht an der Stelle wo sie deklariert ist ausgeführt. Stattdessen kommt sie erst zum Zuge, wenn auf die Elemente der Rückgabeinstanz des LINQ Ausdruckes zugegriffen wird. Dadurch kann man vor der eigentlichen Verwendung der Rückgabe -Elemente des LINQ Ausdruckes noch das zu filternde Objekt verändern. Diese Veränderungen werden von dem LINQ Ausdruck dann auch berücksichtigt:

 

 

int[] integers = new int[8];

 

for (int i = 0; i < 5; i++) //initialisiert integers mit den Zahlen 1 - 5

{

integers[i] = i + 1;

}

 

          

//liefert alle Zahlen aus integers die kleiner 0 sind, also würde //normalerweise keine einzige Zahl zurückgegeben werden

IEnumerable<int> numbers = from number in integers

where number < 0 select number;

 

 

integers[5] = -42;

integers[6] = -7;

integers[7] = -9;

 

foreach (int myNumber in numbers)

{

//gibt - 42, -7 und -9 aus, da jetzt erst auf Elemente von numbers //zugegriffen wird, und damit erst HIER der LINQ Ausdruck über das //integers – Array läuft

Console.WriteLine(myNumber.ToString());

}

 

 

Der LINQ Ausdruck wird in diesem Beispiel innerhalb der foreach Schleife ausgeführt,

da erst hier der Zugriff auf die Elemente von numbers (dem Rückgabewert der LINQ Expression) erfolgt. Folglich gibt das Beispiel die kurz vorher zu integers hinzugefügten negativen Zahlen aus, obwohl es an der eigentlichen Stelle der Deklaration des LINQ Ausdruckes in integers noch keine negativen Zahlen gibt.

Dieses Verhalten von LINQ tritt jedoch nicht immer auf. Bei Verwendung der Aggregat -Operatoren (z.B. Count, Min, Max und Average) wird der LINQ Ausdruck sofort an der Stelle ausgeführt wo er deklariert ist, wie man etwa in folgendem Beispiel sehen kann:

 

 

int[] integers = new int[8];

 

for (int i = 0; i < 5; i++) //initialisiert integers mit den Zahlen 1 - 5

{

integers[i] = i + 1;

}

 

//LINQ Ausdruck wird wegen Count Operator sofort hier ausgeführt

int numbersCount = (from number in integers

where number < 0 select number).Count();

 

integers[5] = -42;

integers[6] = -7;

integers[7] = -9;

 

//gibt 0 aus, da der LINQ Ausdruck oben bei der Deklaration ausgeführt //wurde, und zu diesem Zeitpunkt gab es im integers Array keine Zahlen < 0

Console.WriteLine(numbersCount.ToString());

 

 

Das Beispiel oben gibt diesmal 0 aus, da der LINQ Ausdruck aufgrund des Count Operators direkt an der deklarierten Stelle ausgeführt wird. Somit haben die späteren Änderungen von integers keinerlei Auswirkung auf das Ergebnis der LINQ Expression.

 

Zusätzlich zu den oben beschriebenen Features von LINQ wird es LINQ auch für XML betreffende Klassen (Bezeichnung: XLINQ) sowie für Datenobjekte von ADO.NET (Bezeichnung: DLINQ) geben.

Schlusswort

 

Wie Sie in diesem Whitepaper lesen konnten, wird C# 3.0 mit einigen interessanten Neuerungen aufwarten, Features, die C# noch produktiver machen werden. Dadurch wird die Programmierung in einigen Anwendungsgebieten noch weiter vereinfacht werden – vor allem durch LINQ, das die bisher vorhandene Lücke zwischen der Anwendungsprogammierung und Datenbanken (bzw. Datenbanksprachen) verkleinern wird. Um die Beispiele nachvollziehen zu können oder auch um einfach mit C# 3.0 zu experimentieren brauchen sie das LINQ May 2006 CTP (zu finden unter:

http://www.microsoft.com/downloads/details.aspx?FamilyID=1E902C21-340C-4D13-9F04-70EB5E3DCEEA&displaylang=en  ).

veröffentlicht am 16.04.2007 12:51

Kommentare

 re: Erweiterungen in C# 3.0 vom 16.10.2007 17:07 von Hans-Joachim Sowada
sehr gut!

 re: Erweiterungen in C# 3.0 vom 20.11.2007 23:21 von Claudio Mertz
Kurz und bündig - Danke

 re: Erweiterungen in C# 3.0 vom 12.12.2007 09:39 von Sebastian Dusch
Sind das nicht alles Features vom 3.5 Framework? Zumindest Linq und Lamba Expressions.

 re: Erweiterungen in C# 3.0 vom 12.12.2007 10:39 von Autor
@Sebastian Dusch:
Natürlich sind Linq und Lambda Expressions Features vom .net 3.5 Framework, allerdings sind diese ja in die Sprache C# entsprechend integriert (z.B. das "from" oder "where" Schlüsselwort für Linq) wobei die Art der Integration nicht vom .net Framework 3.5 vorgegeben wird, sondern diese Features entsprechend im gewissen Rahmen unterschiedlich von den jeweiligen Sprachen unterstützt werden können. Daher haben auch Features wie Linq oder Lambda Expressions ihren Platz in einem Artikel über die Sprache C# 3.0.

 re: Erweiterungen in C# 3.0 vom 25.01.2008 14:53 von Freddy
Anonyme Typen:

var person = new { Vorname = "Bill", Nachname = "Gates", Alter = 56};

ist ok, aber ein folgendes

person.Alter = 1;

führt zu einem Fehler:

Error 1 Property or indexer 'AnonymousType#1.Alter' cannot be assigned to -- it is read only

D.h. offensichtlich werden nur Getter in die anonyme Klasse generiert, aber keine Setter...!? (Umgebung: VS2008-en)

 re: Erweiterungen in C# 3.0 vom 25.01.2008 15:52 von Autor
@Freddy
Ja, es scheint tatsächlich nur ein get für die Property erzeugt zu werden. Zu dem Zeitpunkt wo ich den Artikel verfasst habe, wurde das Feature in der C# 3.0 Language Specification (vor dem Releasedatum) aber etwas anders als heute angegeben. Ich werde den Fehler schnellstmöglich korrigieren. Danke für den Hinweis.

 re: Erweiterungen in C# 3.0 vom 17.06.2008 11:54 von name
Beschreibung ist super, aber fehlen noch Automatische Eigenschaften.

# Lorazepam. vom 24.01.2010 17:11 von Lorazepam.
Effect lorazepam side. Lorazepam r. Cheap lorazepam. Lorazepam.

# Tramadol hcl. vom 24.01.2010 20:04 von Best price for tramadol.
Tramadol tablets. Tramadol cheap no rx free overnight shipping. Picture of tramadol 93. Tramadol.

# Buy phentermine. vom 24.01.2010 21:48 von Buy phentermine.
Buy phentermine.

# Doxycycline eye bacteria. vom 25.01.2010 20:21 von Can doxycycline be used fors trep throat.
Doxycycline. Side effects of doxycycline. Idinification of doxycycline hycl.

# Codeine online. vom 26.01.2010 14:50 von Codeine.
Canadian codeine on line. Tylenol 3 codeine. Buy 222 codeine. Codeine.

# Buy tramadol. vom 26.01.2010 21:19 von Buy tramadol.
Buy tramadol.

# Biotek ephedrine. vom 27.01.2010 16:01 von Ephedrine side effects.
Ephedrine faq ephedrine fatloss. Ephedrine. Hydroxycut ephedra ephedrine. Ephedrine phpbb forum. Who sells ephedrine diet pills. Addicted to ephedrine. Interactions between ephedrine and lexapro. Ephedrine hcl.

# Www.weird skin sensations from rx ativan. vom 28.01.2010 18:02 von Ativan.
Www.ativan adverse reaction. Ativan no prescription. Ativan data. The use of ativan in the prehospital setting. Ativan cheap. Www.weird skin sensations from rx ativan. Can you give ativan to a dog. Ativan.

# Buy generic xanax information. vom 29.01.2010 16:13 von Search results buy xanax online.
Buy 180 xanax 2mg. Buy xanax online. Buy xanax online no rx. Buy xanax with mastercard overnight delivery.

# Buy lorazepam. vom 30.01.2010 16:59 von Buy lorazepam.
Buy lorazepam.

# Maxium dose of adderall xr for an adult. vom 31.01.2010 12:08 von Adderall.
Kidney pain and adderall. Adderall.

# Ambien. vom 31.01.2010 17:23 von Ambien.
Purchasing ambien online. Ambien cr addiction. Ambien. Ambien lethal. Buy ambien.

# Clonazepam overnight. vom 31.01.2010 19:25 von Clonazepam.
Clonazepam. Clonazepam overnight no prescription. Buy clonazepam online no prescription. Clonazepam no prescription.

# Soma petroleum. vom 01.02.2010 12:13 von Soma.
Soma addiction and lying.

# Oxycontin dose. vom 02.02.2010 15:17 von Oxycontin.
Hartford oxycontin attorneys. Oxycontin cost. Oxycontin injury attorney columbus.

# Buy fioricet online that ships to missouri. vom 03.02.2010 16:10 von Buy fioricet.
Buy fioricet bloghoster. Buy fioricet online that ships to missouri. Buy fioricet.

# reviews reduced vom 19.02.2010 17:29 von reviews reduced
beta actual changes agreement without effects

Kommentar abgeben

Titel:
Name:
Email:
(wird nicht angezeigt!)
Homepage:
Feedback:
Please add 7 and 2 and type the answer here: