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

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 10 LINQ to Objects
Pfeil 10.1 Einführung in LINQ
Pfeil 10.1.1 Grundlagen der LINQ-Erweiterungsmethoden
Pfeil 10.1.2 Verzögerte Ausführung
Pfeil 10.2 LINQ to Objects
Pfeil 10.2.1 Musterdaten
Pfeil 10.2.2 Allgemeine Syntax
Pfeil 10.2.3 Übersicht über die Abfrageoperatoren
Pfeil 10.2.4 Die »from«-Klausel
Pfeil 10.2.5 Der Restriktionsoperator »where«
Pfeil 10.2.6 Projektionsoperatoren
Pfeil 10.2.7 Sortieroperatoren
Pfeil 10.2.8 Gruppieren mit »GroupBy«
Pfeil 10.2.9 Verknüpfungen mit »Join«
Pfeil 10.2.10 Die Set-Operatoren-Familie
Pfeil 10.2.11 Die Familie der Aggregatoperatoren
Pfeil 10.2.12 Generierungsoperatoren
Pfeil 10.2.13 Quantifizierungsoperatoren
Pfeil 10.2.14 Aufteilungsoperatoren
Pfeil 10.2.15 Elementoperatoren

10 LINQ to Objects

LINQ (Language Integrated Query) ist eine Sprachergänzung von .NET, die mit dem .NET Framework 3.5 und Visual Studio 2008 eingeführt worden ist. LINQ ermöglicht es, über ein neues Abstraktionsmodell Daten abzufragen. Dabei spielt es keine Rolle, welcher Natur die Daten sind: Es kann sich um ein XML-Dokument handeln oder um eine Datenbanktabelle, um eine Excel-Tabelle oder auch um eine Auflistung von herkömmlichen Objekten. Diese Liste könnte beliebig fortgesetzt werden.


Galileo Computing - Zum Seitenanfang

10.1 Einführung in LINQ Zur nächsten ÜberschriftZur vorigen Überschrift

Stellen Sie sich LINQ als eine Erweiterung vor, die dazu dient, den Zugriff auf Daten zu vereinheitlichen und zu vereinfachen. Wegen der unterschiedlichen Datenquellen wurden mehrere LINQ-Implementierungen entwickelt, die als Bestandteil des .NET Frameworks als Provider bereitgestellt werden:

  • LINQ to Objects wird durch den Namespace System.Linq zur Verfügung gestellt und bildet das Fundament aller LINQ-Abfragen. Mit LINQ to Objects lassen sich Auflistungen und Objekte manipulieren, die untereinander in Beziehung gesetzt werden können. Dabei beschränkt sich der Einsatz nicht nur auf benutzerdefinierte Daten.
  • LINQ to XML bietet eine Programmierschnittstelle für XML im Arbeitsspeicher, die das in .NET sprachintegrierte Abfrage-Framework nutzt.
  • LINQ to SQL ist Microsofts LINQ-Provider für das hauseigene Datenbanksystem SQL Server 2005 bzw. 2008.
  • LINQ to ADO.NET besteht aus zwei separaten Technologien: LINQ to DataSet und LINQ to SQL. LINQ to DataSet ermöglicht umfangreichere, optimierte Abfragen der Daten aus einem DataSet, und mit LINQ to SQL können Sie SQL-Server-Datenbankschemas direkt abfragen.

Jeder dieser Provider verfügt über eigene Klassen, um den spezifischen Bedürfnissen der entsprechenden Datenquelle zu entsprechen. Da LINQ eine offene Architektur ist, kann man davon ausgehen, dass das .NET Framework in Zukunft um weitere Provider ergänzt werden wird. In diesem Kapitel werden wir uns ausschließlich den Grundlagen von LINQ to Objects widmen.

Im Folgenden möchte ich Ihnen eine typische LINQ-Abfrage vorstellen, damit Sie einen ungefähren Begriff davon bekommen, was Sie in diesem Kapitel erwartet.


var pers = from p in personen
           where p.Alter > 30
           select new { p.Name, p.Alter };

Gleichwertig ist auch die folgende Formulierung:


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

Diese Abfrage ruft aus einer Liste die Personen ab, die älter als 30 Jahre sind, und gibt in der Ergebnisliste den Namen und das Alter der gefundenen Personen an. Es spielt keine Rolle, woher die Daten in der Liste personen stammen: Es könnte sich beispielsweise um ein Array von Person-Objekten oder um die Ergebnismenge einer Datenbankabfrage handeln. Die Abfragesyntax ist in jedem Fall datenquellenneutral.

Die von LINQ verwendete Syntax ähnelt der, die Sie vielleicht von SQL her kennen. Die Einführung von LINQ mit C# 3.0 zwang das .NET-Entwicklerteam dazu, die .NET-Sprachen zu ergänzen. Dazu gehören Lambda-Ausdrücke, Typinferenz, Objektinitialisierer, anonyme Typen und Erweiterungsmethoden. Diese Sprachfeatures wurden in den vergangenen Kapiteln bereits behandelt.


Galileo Computing - Zum Seitenanfang

10.1.1 Grundlagen der LINQ-Erweiterungsmethoden Zur nächsten ÜberschriftZur vorigen Überschrift

Ehe wir uns intensiver mit LINQ beschäftigen möchte ich Ihnen zeigen, wie ein Abfrageausdruck in der Form, wie er oben gezeigt wurde, zustande kommt. Dazu erzeugen wir ein String-Array mit mehreren Vornamen. 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. 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;
  }
}

So weit funktioniert dieser Code einwandfrei. Was würden Sie aber 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 viele unterschiedliche Methoden wären Sie bereit zu implementieren, um möglichst viele, wenn nicht sogar alle denkbaren Filter 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 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 generische Typ T beschreibt den Typ des Übergabeparameters, TResult den Rückgabetyp. 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 der Lambda-Ausdruck in der Methode GetShortNames zur Auswertung herangezogen wird (vielleicht erinnern Sie sich, ein Lambda-Ausdruck ist im Grunde nichts anderes als eine anonyme Methode). 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 dem Ausdruck Where. Hier handelt es sich um eine Erweiterungsmethode. Sie 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.


Galileo Computing - Zum Seitenanfang

10.1.2 Verzögerte Ausführung topZur vorigen Überschrift

LINQ-Abfragen haben ein besonderes Charakteristikum. Sie werden nämlich nicht sofort ausgeführt, sondern erst, wenn die Ergebnismenge benötigt wird. Das könnte beispielsweise eine foreach-Schleife sein, innerhalb der die Abfrageresultate verarbeitet werden.

Greifen Sie wiederholt auf die Ergebnismenge zu, wird die Abfrage jedes Mal erneut ausgeführt – die Ergebnismenge wird also nicht gecacht. Hat sich in der Zwischenzeit die Datenquelle geändert, erhalten Sie die aktualisierten Daten und profitieren von diesem Verhalten. Andererseits kostet die erneute Ausführung der Abfrage auch Performance.

Ob das Verhalten der verzögerten Ausführung als positiv oder eher als negativ zu bewerten ist, hängt vom Einzelfall ab. In einer Anwendung, die mehrfach die Daten abfragen muss, können Sie mit den Methoden ToArray, ToList oder ToDictionary die Ergebnismenge zwischenspeichern – zumindest solange gewährleistet ist, dass sich die Datenquelle nicht zu häufig ändert. Alle drei Methoden werden auf dem IEnumerable<TSource>-Objekt aufgerufen, oder mit anderen Worten, auf der Ergebnismenge.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2010

Visual C# 2010
Jetzt bestellen


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

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2010
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.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern