Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Über den Autor
Vorwort zur 4. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign und Vererbung
4 Weitere .NET-Datentypen
5 Weitere Möglichkeiten von C#
6 Projektmanagement und Visual Studio 2008
7 Fehlerbehandlung und Debugging
8 LINQ
9 Multithreading und asynchrone Methodenaufrufe
10 Arbeiten mit Dateien und Streams
11 Serialisierung
12 Einige wichtige .NET-Klassen
13 Grundlagen zum Erstellen einer Windows-Anwendung
14 Die wichtigsten Steuerelemente
15 Tastatur- und Mausereignisse
16 MDI-Anwendungen
17 Grafische Programmierung mit GDI+
18 Das Drucken (Printing)
19 Steuerelemente entwickeln
20 Programmiertechniken
21 WPF – die Grundlagen
22 Die Layoutcontainer
23 Die WPF-Controls
24 Konzepte von WPF
25 ADO.NET – die Verbindung zu einer Datenbank herstellen
26 Die Datenbankabfrage
27 Der SqlDataAdapter
28 Daten im lokalen Speicher – das DataSet
29 Eine Datenbank aktualisieren
30 Stark typisierte DataSets
31 Weitergabe von Anwendungen
Stichwort

Download:
- ZIP, ca. 13,6 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2008 von Andreas Kuehnel
Das umfassende Handbuch
Buch: Visual C# 2008

Visual C# 2008
geb., mit DVD
1.366 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1172-7
Pfeil 8 LINQ
Pfeil 8.1 Was ist LINQ?
Pfeil 8.2 Neue Sprachfeatures in C# 2008
Pfeil 8.2.1 Implizit typisierte Variablen (Typinferenz)
Pfeil 8.2.2 Lambda-Ausdrücke
Pfeil 8.2.3 Erweiterungsmethoden
Pfeil 8.2.4 Anonyme Typen
Pfeil 8.2.5 C# 3.0 und LINQ-Abfragen
Pfeil 8.3 LINQ to Objects
Pfeil 8.3.1 Musterdaten
Pfeil 8.3.2 Die Abfrage-Syntax
Pfeil 8.3.3 Übersicht über die Abfrageoperatoren
Pfeil 8.3.4 Die »from«-Klausel
Pfeil 8.3.5 Der Restriktionsoperator »where«
Pfeil 8.3.6 Die Projektionsoperatoren
Pfeil 8.3.7 Sortieroperatoren
Pfeil 8.3.8 Gruppieren mit »GroupBy«
Pfeil 8.3.9 Verknüpfungen mit »Join«
Pfeil 8.3.10 Die Set-Operatoren-Familie
Pfeil 8.3.11 Die Familie der Aggregatoperatoren
Pfeil 8.3.12 Generierungsoperatoren
Pfeil 8.3.13 Quantifizierungsoperatoren
Pfeil 8.3.14 Aufteilungsoperatoren
Pfeil 8.3.15 Die Elementoperatoren


Galileo Computing - Zum Seitenanfang

8.2 Neue Sprachfeatures in C# 2008 Zur nächsten ÜberschriftZur vorigen Überschrift


Galileo Computing - Zum Seitenanfang

8.2.1 Implizit typisierte Variablen (Typinferenz) Zur nächsten ÜberschriftZur vorigen Überschrift

Bei der Deklaration einer Variablen müssen Sie deren Datentyp angeben. So haben Sie es gelernt. Mit Einführung von C# 3.0 hat sich das geändert. Die Typinferenz gestattet es Ihnen, eine Variable mit dem neuen Schlüsselwort var zu deklarieren, ohne dabei den Datentyp angeben zu müssen.

var x = 5;

Das Schlüsselwort var bedeutet nicht, dass die Variable erst zur Laufzeit gebunden wird. Es bedeutet nur, dass der Compiler den am besten passenden Datentyp aus dem Ausdruck rechts vom Zuweisungsoperator ableitet. In unserem Beispiel wäre es ein Integer. Der Compiler behandelt die Variable dann so, als wäre sie von diesem Typ deklariert worden.

Bei dem abgeleiteten Typ kann es sich um einen integrierten Typ, einen anonymen Typ, einen benutzerdefinierten Typ, einen in der .NET Framework-Klassenbibliothek definierten Typ oder um einen Ausdruck handeln.

Hier folgen noch einige Beispiele, die den Einsatz implizit typisierter Variablen demonstrieren. Sie werden vermutlich die beiden letzten Beispiele noch nicht verstehen, weil ich Ihnen beispielsweise auch noch nicht erklärt habe, was unter einem anonymen Typ zu verstehen ist. Nur Geduld, ich werde das später noch nachholen.

// x wird als Integer kompiliert 
var x = 5;

// s wird als String kompiliert 
var s = "Aachen";

// arr wird als int[]-Array kompiliert 
var arr = new[] { 0, 1, 2 };

// a wird als IEnumerable<Customer> kompiliert 
var a = from c in Products 
        where c.CategoryID == 2 
        select c;

// z wird als anonymer Typ kompiliert 
var z = new { Name = "Terry", Age = 34 };

// liste wird als List<int> kompiliert 
var liste = new List<int>();

Das Konzept implizit typisierter Variablen hat einige Einschränkungen:

  • Die Variable muss eine lokale sein.
  • Die Initialisierung muss bei der Deklaration erfolgen.
  • Einer implizit typisierten Variablen darf nicht null zugewiesen werden.
  • Ein Methodenparameter darf nicht mit var deklariert werden.
  • Der Rückgabetyp einer Methode darf ebenfalls nicht var sein.

Die Verwendung implizit typisierter Variablen ist nicht nur auf die Verwendung als lokale Variable beschränkt. Sie können sie auch in einer for- oder foreach-Schleife verwenden, wie in den beiden folgenden Codefragmenten gezeigt wird.

for (var x = 0; x < 100; x++)... 
foreach (var element in liste)...

Sie werden sich vermutlich nun fragen, wozu implizit typisierte Variablen dienen. Betrachten Sie sie einfach nur als syntaktisches Hilfsmittel, das Sie optional einsetzen können. Erforderlich werden solchermaßen deklarierte Variablen im Zusammenhang mit LINQ-Abfrageausdrücken.


Galileo Computing - Zum Seitenanfang

8.2.2 Lambda-Ausdrücke Zur nächsten ÜberschriftZur vorigen Überschrift

In Kapitel 4 hatte ich Ihnen im Zusammenhang mit den Delegates das Beispielprogramm EinfachesDelegate vorgestellt. Zur Erinnerung: An der Konsole wurde der Anwender zur Eingabe von zwei Zahlen aufgefordert. Anschließend konnte der Benutzer entscheiden, ob er die beiden Zahlen addieren oder subtrahieren möchte. Später wurden im gleichen Kapitel anstelle der Delegates anonyme Methoden verwendet. Hier noch einmal die Passage, die für uns im Weiteren von Interesse ist:

... 
if (wahl == "A") 
  // Definition der anonymen Methode für die Addition 
  process = delegate(double x, double y) { return x + y;}; 
else if (wahl == "S") 
  // Definition der anonymen Methode für die Subtraktion 
  process = delegate(double x, double y) { return x - y; }; 
...

Bei einem Lambda-Ausdruck handelt es sich um eine anonyme Methode, die Ausdrücke und Anweisungen enthalten und für die Erstellung von Delegates verwendet werden kann.

Mithilfe von Lambda-Ausdrücken können Sie den Code von oben auch wie folgt formulieren:

... 
if (wahl == "A") 
  process = (double x, double y) => { return x + y;}; 
else if (wahl == "S") 
  process = (double x, double y) => { return x - y; }; 
...

Die beiden Lambda-Ausdrücke in diesem Codefragement sind

(double x, double y) => { return x + y;};

und

(double x, double y) => { return x - y; }

Lamda-Ausdrücke verwenden den Operator =>. Links davon werden die Eingabeparameter angegeben, rechts davon der Ausdruck oder ein Anweisungsblock. Das in der ursprünglichen Fassung vorhandene Schlüsselwort delegate taucht nicht mehr auf.

Der Anweisungsrumpf eines Lambda-Ausdrucks benötigt eine geschweifte Klammer wie jeder andere Anweisungsblock auch und kann beliebig viele Anweisungen enthalten. Häufig anzutreffen sind Lambda-Ausdrücke, deren einzige Anweisung ein return ist. In einem solchem Fall dürfen Sie die return-Anweisung weglassen und können gleichzeitig auch auf die geschweiften Klammern verzichten.

... 
if (wahl == "A") 
  process = (double x, double y) => x + y; 
else if (wahl == "S") 
  process = (double x, double y) => x - y; 
...

Bisher scheint es so zu sein, dass die Einführung der Lambda-Ausdrücke nur rein syntaktischer Natur ist. Dem ist aber nicht so. Sehen Sie sich dazu das folgende Codefragment an:

... 
if (wahl == "A") 
  process = (x, y) => x + y; 
else if (wahl == "S") 
  process = (x, y) => x - y; 
...

Beachten Sie, dass nun die Angabe der Parametertypen entfernt worden ist. Es handelt sich jetzt um implizit typisierte Parameter, und der Compiler leitet die Parametertypen richtig ab. Vorausgesetzt werden muss dabei natürlich, dass der Operator »+« für den konkreten Typ von x und y definiert ist. In unserem Beispiel ist das der Fall.

Der Lamda-Ausdruck

(x, y)  => x + y

hat zwei Parameter, die in runden Klammern eingeschlossen und durch ein Komma getrennt sind. Liegt nur ein Parameter vor, können die runden Klammern aus der Parameterliste entfernt werden:

x  => x + x

Bei einem Lambda-Ausdruck mit leerer Parameterliste müssen jedoch die runden Klammen angegeben werden:

()  => a * b

Ein Lambda-Ausdruck, der lediglich eine return-Anweisung enthält, wird als Ausdrucksrumpf bezeichnet.

Projektion und Prädikat

Der Datentyp der Rückgabe eines Lambda-Ausdrucks kann sich vom Datentyp des Parameters unterscheiden. Liegt ein solcher Lambda-Ausdruck vor, wird von einer Projektion gesprochen. Die folgende Anweisung zeigt eine solche. Dabei wird eine Zeichenfolge übergeben und deren Länge geprüft. Der Rückgabewert ist vom Typ Integer.

(str) => str.Length

Ein Prädikat hingegen liefert einen booleschen Wert als Ergebnis einer Operation:

(alter) => alter > 65

Galileo Computing - Zum Seitenanfang

8.2.3 Erweiterungsmethoden Zur nächsten ÜberschriftZur vorigen Überschrift

Erweiterungsmethoden stellen ein wenig das strenge Konzept der Objektorientierung auf den Kopf. Unsere Aussage war bisher immer, dass die zu einer Klasse gehörenden Methoden in dieser Klasse implementiert werden müssen und an die ableitenden Klassen vererbt werden. Die in C# 3.0 eingeführten Erweiterungsmethoden weichen dieses Prinzip auf, indem auch außerhalb einer Klasse Methoden definiert werden können, die sich wie eine Instanzmethode aufrufen lassen.

Nehmen wir dazu das Beispiel der hinlänglich bekannten Klasse Circle. Vielleicht genügt uns das Angebot an Methoden nicht, weil wir noch zusätzlich gern eine Methode hätten, um auf Grundlage des Radius das Kugelvolumen zu berechnen, beispielsweise so:

Circle kreis = new Circle(5); 
Console.WriteLine("Kugelvolumen = {0}", kreis.GetVolume());

Durch Bereitstellung einer Erweiterungsmethode ist das kein Problem.

static class Extensionmethods { 
  // Erweiterungsmethode zur Berechnung des Kugelvolumens 
  // eines Objekts vom Typ 'Circle' 
  public static double GetVolume(this Circle kreis) { 
    return Math.Pow(kreis.Radius, 3) * Math.PI * 4 / 3; 
  } 
}

Erweiterungsmethoden werden in static-Klassen implementiert und müssen selbst als public static signiert sein. Beachten Sie bitte, dass trotz der static-Definition Erweiterungsmethoden wie Instanzmethoden aufgerufen werden. Der erste Parameter in der Parameterliste muss das Schlüsselwort this vor dem ersten Parametertyp aufweisen. Hier wird der Typ angegeben, der um die genannte Methode erweitert wird. In unserem Beispiel handelt es sich um Circle. Sie können beliebig viele Erweiterungsmethoden für einen Typ schreiben, ganz so, wie Sie es benötigen. Üblicherweise werden Erweiterungsmethoden in eigens dafür vorgesehenen spezifischen Namespaces definiert. Diese sollten innerhalb des Projekts mit using importiert werden.

Mit Erweiterungsmethoden können Sie alle Klassen beliebig erweitern und so an Ihre spezifischen Anforderungen anpassen. LINQ macht sehr intensiven Gebrauch von Erweiterungsmethoden, wie Sie im Laufe dieses Kapitels noch sehen werden. Dem Einsatz sind aber auch Grenzen gesetzt, denn Erweiterungsmethoden können nur public-Member der zu erweiternden Klasse aufrufen.

Wird eine Klasse um eine Erweiterungsmethode ergänzt, vererbt sich diese auch an die abgeleitete Klasse weiter. Bezogen auf unser Beispiel oben könnten Sie demnach GetVolume auch auf ein Objekt vom Typ GraphicCircle aufrufen. Hinsichtlich der Überladungsfähigkeit gelten dieselben Regeln wie bei den herkömmlichen Methoden.

Die Prioritätsregeln

Da Erweiterungsmethoden auch von Entwicklern geschrieben werden, die nicht Urheber der erweiterten Klasse sind, haben Erweiterungsmethoden nur eine untergeordnete Priorität. Betrachten Sie dazu das folgende Beispiel, in dem die Klasse Circle um die Methode Draw erweitert wird.

static class Extensionmethods { 
  // Erweiterungsmethode zur Berechnung des Kugelvolumens 
  // eines Objekts vom Typ 'Circle' 
  public static double GetVolume(this Circle kreis) { 
    return Math.Pow(kreis.Radius, 3) * Math.PI * 4 / 3; 
  }

  // Erweiterungsmethode 'Draw' 
  public static void Draw(this Circle kreis) { 
    Console.WriteLine("Draw in Erweiterungsmethode."); 
  } 
}

Circle ist um die Methode Draw erweitert worden, die sich an GraphicCircle weitervererbt. Da eine gleichnamige Instanzmethode in GraphicCircle existiert, muss die Entscheidung getroffen werden, welche der beiden zur Ausführung kommt: Es handelt sich definitiv um die Draw-Methode der Klasse GraphicCircle.

static void Main(string[] args) { 
  Circle kreis = new Circle(5); 
  kreis.Draw(); 
  GraphicCircle g = new GraphicCircle(); 
  g.Draw(); 
}

Die Ausgabe dieses Codefragments wird so lauten:

Draw in der Erweiterungsmethode. 
Der Kreis wird gezeichnet.

Ob eine Erweiterungsmethode aufgerufen wird, hängt davon ab, ob eine gleichnamige Instanzmethode existiert. Wie Sie gesehen haben, hat eine Instanzmethode in jedem Fall Priorität vor einer gleichnamigen Erweiterungsmethode.

Die Erweiterungsmethode einer Klasse kann stets durch eine spezifischere Version ersetzt werden, die für einen Typ definiert ist. Gewissermaßen haben wir es dabei mit einer Überschreibung zu tun. Angenommen, die Klasse Object sei um die Methode Display erweitert worden. Damit steht jeder Klasse die Erweiterungsmethode zur Verfügung – soweit sie sich im aktuellen Namespace befindet oder in einem Namespace, der mit using importiert wird. Eine spezifische Version von Display kann aber auch für alle Objekte vom Typ Circle bereitgestellt werden. Die Circle-Version überdeckt in diesem Fall die »geerbte« Erweiterungsmethode der Klasse Object.

static class Extensionmethods { 
  public static void Display(this object obj) { 
    Console.WriteLine(obj.ToString()); 
  }

  public static void Display(this Circle kreis) { 
    Console.WriteLine("Kreis mit Radius {0}", kreis.Radius); 
  } 
}

Die Spezialisierung einer Erweiterungsmethode für einen bestimmten Typ setzt sich auch in den abgeleiteten Klassen durch. Damit wird ein GraphicCircle-Objekt ebenfalls von der spezifischen Version profitieren, es sei denn, für den abgeleiteten Typ gibt es wiederum eine eigene Version der Erweiterungsmethode, die noch spezialisierter ist.

// Aufruf der Erweiterungsmethode 'Display' 
Circle kreis = new Circle(5); 
kreis.Display(); 
GraphicCircle g = new GraphicCircle(3); 
g.Display();

Generische Erweiterungsmethoden

Erweiterungsmethoden lassen sich generisch prägen. Damit wird es möglich, eine Erweiterungsmethode beispielsweise nur für eine bestimmte Gruppe von Objekten zur Verfügung zu stellen. Der folgende Code beschreibt die Erweiterungsmethode GetFlaechen. Diese Methode erweitert alle Arrays vom Typ GeometricObject und somit auch Arrays vom Typ Circle, Rectangle usw.

class Program { 
  static void Main(string[] args) { 
    GeometricObject[] geoArr = new GeometricObject[3]; 
    geoArr[0] = new Circle(5); 
    geoArr[1] = new GraphicCircle(9); 
    geoArr[2] = new Rectangle(12, 7); 
    geoArr.GetFlaechen(); 
    Console.ReadLine(); 
  } 
}

static class Extensionmethods { 
  public static void GetFlaechen<T>(this T[] objects) 
                                 where T : GeometricObject { 
    foreach (GeometricObject geoObj in objects) 
      Console.WriteLine(geoObj.GetFlaeche()); 
  } 
}

Richtlinien für Erweiterungsmethoden

Mit den Erweiterungsmethoden wird uns ein sehr interessantes Feature an die Hand gegeben, um vorhandene Klassen zu erweitern. Im Allgemeinen sollten Sie aber darauf achten, dass Sie nur dann Erweiterungsmethoden implementieren, wenn es unbedingt notwendig ist. Nach Möglichkeit sollten Sie besser eine Klasse ableiten, anstatt eine Erweiterungsmethode bereitzustellen.

Wenn Sie eine Klassenbibliothek implementieren, sollten Sie es grundsätzlich vermeiden, die darin definierten Typen um Erweiterungsmethoden zu ergänzen, um das Konzept der Objektorientierung nicht unnötig aufzuweichen. Erweiterungsmethoden sind nur dann ein sinnvolles Feature, wenn Ihnen anderweitig keine Möglichkeit mehr bleibt, beispielsweise weil Sie eine sealed-, also nicht ableitbare Klasse erweitern möchten.

Sie sollten sich aber auch darüber im Klaren sein, dass die Versionsänderung einer Assembly dazu führen kann, dass eine zuvor für eine Klasse bereitgestellte Erweiterungsmethode wirkungslos wird, weil die entsprechende Klasse um eine gleichnamige Instanzmethode ergänzt worden ist.


Galileo Computing - Zum Seitenanfang

8.2.4 Anonyme Typen Zur nächsten ÜberschriftZur vorigen Überschrift

Bei der Instanziierung einer Klasse wird üblicherweise ein Konstruktor aufgerufen. Werden dabei Argumente übergeben, muss der Konstruktor des Typs passend überladen sein.

Circle kreis = new Circle(23);

In C# 3.0 wurde eine kürzere Syntaxform für die Objektinitialisierung eingeführt. Die folgende Anweisung ist funktional absolut gleichwertig mit der zuvor gezeigten Instanziierung:

Circle kreis = new Circle { Radius = 23 };

Die Namen, die der Initialisierungsliste zugewiesen werden, können öffentlich definierte Felder oder Eigenschaftsmethoden sein, die in der entsprechenden Klasse enthalten sind. Diese Aufrufsyntax gestattet darüber hinaus die Angabe eines nicht in der Klasse enthaltenen Konstruktors. Angenommen, in GraphicCircle gäbe es noch ein public Feld namens Color vom gleichnamigen Typ Color, könnte ein Objekt vom Typ GraphicCircle auch wie folgt erzeugt werden:

GraphicCircle graph = new GraphicCircle() { Color = Color.Blue };

Neben dieser Neuerung in C# 3.0 können Sie nun auch ein Objekt erstellen, ohne den Typ explizit anzugeben. Dabei wird eine neue Klasse erstellt – ein sogenannter anonymer Typ:

var obj = new { Name = "Peter", Ort = "Hamburg" };

Die so generierte Klasse hat zwei private Felder und zwei öffentliche Eigenschaftsmethoden, die Name und Ort lauten. Das Objekt der anonymen Klasse wird anschließend einer implizit typisierten Variablen zugewiesen und kann über die Referenz obj abgefragt werden. Wegen der engen Beziehung zwischen var und dem anonymen Typ kann ein solcher nur lokal in einer Methode und nicht auf Klassenebene erzeugt werden.

Wenn Sie einen weiteren anonymen Typ erzeugen und dabei gleich benannte Parameter angeben, sind beide typgleich. Allerdings ist dabei nicht nur der Parameterbezeichner ausschlaggebend, sondern darüber hinaus auch die Reihenfolge der Parameter. Im folgenden Codefragment sind die beiden Referenzen obj1 und obj2 typgleich; obj3 weist bei der Initialisierung jedoch eine andere Reihenfolge auf und wird daher als neuer anonymer Typ bewertet.

var obj1 = new { Name = "Peter", Ort = "Hamburg" }; 
var obj2 = new { Name = "Uwe", Ort = "München" }; 
var obj3 = new { Ort = "Berlin", Name = "Hans" };

Sie können sich das bestätigen lassen, indem Sie die von Object geerbte Methode GetType aufrufen.

Console.WriteLine(obj1.GetType()); 
Console.WriteLine(obj2.GetType()); 
Console.WriteLine(obj3.GetType());

Die Ausgabe wird wie folgt lauten:

<>f__AnonymousType0’2[System.String,System.String] 
<>f__AnonymousType0’2[System.String,System.String] 
<>f__AnonymousType1’2[System.String,System.String]

Galileo Computing - Zum Seitenanfang

8.2.5 C# 3.0 und LINQ-Abfragen topZur vorigen Überschrift

Nachdem ich Ihnen die neuen, mit LINQ in Verbindung stehenden Sprachfeatures von C# 3.0 vorgestellt habe, möchte ich in diesem Abschnitt die Brücke zu den LINQ-Abfragen schlagen. Sie werden dabei schon ansatzweise erkennen, dass es notwendig war, die vorher erörterten Sprachergänzungen vorzunehmen, um eine Abfragesprache bereitzustellen.

Zuerst möchte ich Ihnen an einem Beispiel zeigen, wie Sie mit einem als Prädikat ausgebildeten Lambda-Ausdruck deutlich kürzeren und flexibleren Programmcode schreiben können. In dem Beispiel wird ein String-Array mit mehreren Vornamen erzeugt. Unser Ziel soll es sein, nur die Namen auszugeben, die einer bestimmten Maximallänge entsprechen. Für die Ausgabe soll eine Methode namens GetShortNames implementiert werden. Normalerweise würde die Überprüfung der Länge der einzelnen Namen in dieser Methode codiert werden. Um aber möglichst flexibel zu sein, wird die Überprüfung in eine andere Methode ausgelagert, die TestName lauten soll. Der Methode GetShortNames wird neben dem Zeichenfolge-Array auch ein Delegate auf TestName übergeben.

class Program { 
  delegate bool MyDelegate(string name);

  static void Main(string[] args) { 
    string[] arr = { "Peter", "Uwe", "Willi", "Udo" }; 
    MyDelegate del = TestName; 
    GetShortNames(arr, del); 
    Console.ReadLine(); 
  }

  static void GetShortNames(string[] arr, MyDelegate del) { 
    foreach (string name in arr) 
      if (del(name)) Console.WriteLine(name); 
  }

  static bool TestName(string name) { 
    if (name.Length < 4) return true; 
    return false; 
  } 
}

Was würden Sie machen, wenn Sie in einem anderen Kontext nicht alle Namen selektieren wollen, die weniger als vier Buchstaben aufweisen, sondern beispielsweise mehr als sieben? Richtig, Sie würden eine weitere Methode bereitstellen, die genau das leistet. Und nun eine ganz gemeine Frage: Wie viel unterschiedliche Methoden wären Sie bereit zu implementieren, um möglichst viele, wenn nicht sogar alle denkbaren Selektierungen zu berücksichtigen?

Aber es geht auch anders, denn dasselbe Ergebnis erreichen Sie, wenn Sie einen Lambda-Ausdruck benutzen. Der Code zur Überprüfung der Zeichenfolgelänge wird hierbei direkt in der Parameterliste von GetShortNames aufgeführt.

class Program { 
  static void Main(string[] args) { 
    string[] arr = { "Peter", "Uwe", "Willi", "Udo" }; 
    GetShortNames(arr, name => name.Length < 4); 
    Console.ReadLine(); 
  }

  static void GetShortNames<T>(T[] names, Func<T, bool> getNames) { 
    foreach (T name in names) 
      if (getNames(name)) 
        Console.WriteLine(name); 
  } 
}

Beachten Sie bitte im letzten Beispiel den zweiten Parameter der Methode GetShortNames. Dessen Typ Func<T, bool> wird durch das .NET Framework bereitgestellt. Dabei handelt es sich um ein Delegate. Schauen wir uns die Definition dieses Delegates an:

public delegate TResult Func<T, TResult>(T arg)

Dieses generische Delegate kann auf eine Methode zeigen, die einen Parameter entgegennimmt. Der Rückgabetyp TResult ist ebenfalls generisch. Das Besondere an diesem Delegate ist, dass ihm ein Lambda-Ausdruck zugewiesen werden kann:

Func<T, bool> getNames = name => name.Length < 4

Das Ergebnis der Operation ist ein boolescher Wert. Delegates dieser Art (wie hier Func) können Sie natürlich auch selbst definieren.

Wichtig ist, dass Sie erkennen, dass die Methode GetShortNames jetzt mit ganz unterschiedlichen Prädikaten aufgerufen werden kann. Vielleicht wollen Sie beim nächsten Mal alle Namen selektieren, die mit dem Buchstaben »H« beginnen. Kein Problem: Sie brauchen dazu keine weitere Methode zu schreiben und können die vorliegende benutzen, da in der Methode GetShortNames der Lambda-Ausdruck zur Auswertung herangezogen wird. Nur über die Namensgebung der Methode sollten Sie sich noch einmal Gedanken machen …

Rufen wir uns an dieser Stelle noch einmal das einführende LINQ-Beispiel ins Gedächtnis zurück:

var pers = personen 
          .Where( p => p.Alter > 30 ) 
          .Select( p => new {p.Name, p.Alter});

Widmen Sie Ihre Aufmerksamkeit hier der Methode Where. Sie ist als Erweiterungsmethode implementiert und beschreibt die Operation, aufgrund derer Daten aus der Liste personen selektiert werden sollen. Was der Methode als Parameter übergeben wird, ist eine Operation, die einen booleschen Wert als Resultat liefert. Das erinnert sehr stark an unsere Methode GetShortNames. Sehen wir uns die Signatur von Where an: Sie ähnelt der unserer Methode GetShortNames:

public static IEnumerable<TSource> Where<TSource>( 
                          this IEnumerable<TSource> source, 
                          Func<TSource, bool> predicate);

Der erste Parameter kennzeichnet Where als Erweiterungsmethode für alle Typen, die die Schnittstelle IEnumerable<T> implementieren. Der zweite Parameter ist ein Delegate, das im ersten generischen Parameter den Typ der Datenquelle beschreibt. Der zweite Typparameter kennzeichnet den Rückgabewert Boolean.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2008






Visual C# 2008
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Coding for Fun






 Coding for Fun


Zum Katalog: Einstieg in Visual C# 2008






 Einstieg in
 Visual C# 2008


Zum Katalog: Videotraining - Visual C# 2008






 Videotraining -
 Visual C# 2008


Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2008






 Fortgeschrittene
 Programmierung mit
 Visual C# 2008


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Visual Basic 2008






 Visual Basic 2008


Zum Katalog: Einstieg in XML






 Einstieg in XML


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de