Während es in früheren Versionen von C# unter Umständen sehr aufwändig war, einzelne
Elemente innerhalb einer Aufzählung zu suchen oder diese zu sortieren, stellt C# dafür seit
der Version 3.0 eine eigene Abfragesprache zur Verfügung, die als Language Integrated Query,
abgekürzt Linq, bezeichnet wird.
Um beispielsweise aus einem Array, das Elemente des Typs
string
enthält, alle diejenigen zu ermitteln, deren Wert mit einem bestimmten Buchstaben beginnt
und diese alphabetisch sortiert auszugeben, wurden zumindest eine Schleife und zahlreiche
if-Anweisungen benötigt.
Seit der Version 3.0 von C# gibt es dafür eine eigene Abfragesprache, deren Syntax sich
an der Datenbanksprache SQL orientiert, die aber über vollständige Unterstützung in C# und
Visual Studio verfügt. Durch die Integration in C# wird Linq ebenso wie der übrige Code
durch den Compiler in MSIL übersetzt, so dass auf Linq basierende Abfragen auf Fehler
überprüft werden können. Im Gegensatz zu klassischen Abfragen, die beispielsweise als
Zeichenketten innerhalb von C# vorliegen, können Fehler auf diese Art bereits vor der
Ausführung erkannt werden.
Außerdem werden in Linq geschriebene Abfragen ebenfalls von der Common Language Runtime
ausgeführt und nutzen daher wie C# ebenfalls die Vorteile von verwalteter Ausführung.
Die einfachste Abfrage, die in Linq geschrieben werden kann, ermittelt alle Elemente aus
einer Aufzählung, gibt also die Aufzählung selbst zurück, ohne diese zu durchsuchen oder
zu sortieren. Um die Fähigkeiten von Linq nutzen zu können, muss der Namensraum System.Linq
eingebunden werden, der sich in der Assembly System.Core befindet.
Eine Abfrage wird in Linq mit Hilfe des Schlüsselwortes
from eingeleitet,
dem ein Bezeichner für ein einzelnes Element folgt. Die Wahl dieses Bezeichners ist
beliebig und vergleichbar mit der Wahl des Bezeichners innerhalb einer
foreach-Schleife.
Im Anschluss wird mit Hilfe des Schlüsselwortes
in angegeben, aus
welcher Aufzählung die Elemente stammen, mit dem Schlüsselwort
select
wird schließlich das jeweilige Element als relevant für die Ergebnismenge ausgewählt.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors.
var result =
from c in colors
select c;
// Print all colors to the console.
foreach (var color in result)
{
Console.WriteLine(color);
}
}
}
}
|
Obwohl die Ergebnismenge nur aus Elementen des Typs
string
besteht, ist es in der Praxis üblich, den Typ einer in Linq geschriebenen Abfrage mit
Hilfe von
var zu definieren.
Ebenso könnte die Schleifenvariable der
foreach-Schleife als
string definiert werden, da jedes einzelne Element diesem Typ
entspricht, aber auch dies ist in der Praxis unüblich.
Um die Ergebnismenge zu sortieren, kann das Schlüsselwort
orderby
verwendet werden, wobei nach diesem ein Ausdruck angegeben werden muss, welcher die
Sortierreihenfolge definiert. Es ist also nicht nur möglich, nach dem Element an sich zu
sortieren, sondern es kann beispielsweise auch eine Eigenschaft oder ein Delegat angegeben
werden, der die Sortierung vornimmt.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors in alphabetical order.
var result =
from c in colors
orderby c
select c;
// Print all colors to the console.
foreach (var color in result)
{
Console.WriteLine(color);
}
}
}
}
|
Alternativ zu der aufsteigenden Sortierung können die Elemente der Ergebnismenge durch
die Angabe des zusätzlichen Schlüsselwortes
descending auch
absteigend sortiert werden.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors in reversed alphabetical order.
var result =
from c in colors
orderby c descending
select c;
// Print all colors to the console.
foreach (var color in result)
{
Console.WriteLine(color);
}
}
}
}
|
Zusätzlich kann die Ergebnismenge mit dem Schlüsselwort
where
durchsucht werden, so dass nur einige Elemente in der Ergebnismenge enthalten sind. Im
folgenden Beispiel werden beispielsweise nur die Elemente in die Ergebnismenge aufgenommen,
deren Anfangsbuchstabe ein R ist.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors whose name starts with an R
// in reversed alphabetical order.
var result =
from c in colors
where c.StartsWith("R")
orderby c descending
select c;
// Print all colors to the console.
foreach (var color in result)
{
Console.WriteLine(color);
}
}
}
}
|
Falls der Typ der Elemente der Ergebnismenge kein einfacher Typ, sondern ein komplexer
Typ wie beispielsweise ein Objekt ist, kann es gewünscht sein, nicht das gesamte Element
in die Ergebnismenge aufzunehmen, sondern nur eine oder mehrere Eigenschaften.
Sofern nur eine einzelne Eigenschaft als Element in die Ergebnismenge aufgenommen werden
soll, genügt es, diese an Stelle des eigentlichen Objekts bei
select
anzugeben. Sollen statt dessen mehrere Eigenschaften verwendet werden, können diese mit
Hilfe von Objektinitialisierern in ein neues Objekt von einem anonymen Typ zusammengeführt
werden.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an arrays of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors whose name starts with R in
// reversed alphabetical order, and limit the
// result set to the name and length of the
// colors.
var result =
from c in colors
where c.StartsWith("R")
orderby c descending
select new { Name = c, c.Length };
// Print all colors to the console.
foreach (var color in result)
{
Console.WriteLine(
color.Name + " (" + color.Length + ")");
}
}
}
}
|
Spätestens an dieser Stelle wird deutlich, warum der Datentyp bei Linq nie spezifisch,
sondern in der Regel mit dem Schlüsselwort
var definiert wird.
Ändert sich die Auswahl der in der Ergebnismenge enthaltenen Eigenschaften, so müssen
die verwendeten Typen nicht angepasst werden.
Mit Linq ist es jedoch nicht nur möglich, einzelne Elemente in einer Ergebnismenge
zusammenzufassen, zusätzlich kann diese Menge ihrerseits gruppiert werden. Dazu dient
das Schlüsselwort
group, das immer in Kombination mit dem
Schlüsselwort
by verwendet werden muss, und das angibt, welche
Elemente wie gruppiert werden sollen. Wird
group innerhalb einer
Abfrage verwendet, so kann diese Abfrage kein
select
enthalten.
Eine auf diese Art erzeugte Gruppe von Elementen enthält das Kriterium, mit dessen Hilfe
sie erstellt wurde, in der Eigenschaft Key und kann ihrerseits wiederum als Quelle für
eine Schleife oder eine weitere Abfrage dienen. Im folgenden Beispiel werden jeweils
all jene Elemente zu einer Gruppe zusammengefasst, deren Namen die gleiche Länge haben.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors ordered alphabetically and put
// them in groups depending on the length of the
// color's name.
var result =
from c in colors
orderby c
group c by c.Length;
// Iterate over all groups.
foreach (var group in result)
{
// Print the group's key to the console.
Console.WriteLine(group.Key);
// Iterate over all colors within the group.
foreach (var color in group)
{
// Print the color to the console.
Console.WriteLine(color);
}
}
}
}
}
|
Neben den Möglichkeiten, die sich direkt aus der Verwendung solcher Abfragen ergeben,
erweitert Linq sämtliche Aufzählungstypen mit Hilfe von Erweiterungsmethoden um weitere
Methoden zur Manipulation der Ergebnismenge.
Soll beispielsweise nur das erste Element einer Ergebnismenge ausgewertet werden, so kann
dies mit Hilfe der Methode First abgerufen werden.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors ordered alphabetically.
var result =
from c in colors
orderby c
select c;
// Get the first color from the result.
string color = result.First();
}
}
}
|
Ebenso kann eine gewisse Anzahl an ersten Elementen abgerufen werden, wozu die Methode Take
dient. Im folgenden Beispiel werden immer nur die ersten beiden Elemente der Ergebnismenge
zurückgegeben, unabhängig davon, wie viele Elemente tatsächlich enthalten sind.
C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
using System;
using System.Linq;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define an array of colors.
string[] colors =
new string[] { "Red", "Green", "Blue" };
// Get all colors ordered alphabetically.
var result =
from c in colors
orderby c
select c;
// Get the two top colors from the result.
var topColors = result.Take(2);
}
}
}
|
Intern werden in Linq geschriebene Abfragen in Aufrufe von Erweiterungsmethoden und
Lambdaausdrücke umgewandelt. Dies geschieht ähnlich wie bei der
foreach-Schleife im Hintergrund durch den Compiler, ohne dass
der Entwickler dies bemerkt.
Beispielsweise wird die Abfrage
C# |
1
2
3
4
5
6
7
|
// Get all colors whose name starts with an R in reversed
// alphabetical order.
var result =
from c in colors
where c.StartsWith("R")
orderby c descending
select c;
|
von C# in
C# |
1
2
3
4
5
|
// Get all colors whose name starts with an R in reversed
// alphabetical order.
var result =
colors.Where(c => c.StartsWith("R"))
.OrderByDescending(c => c).Select(c => c);
|
umgewandelt.