Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Die group Klausel gibt eine Abfolge von IGrouping<TKey,TElement> Objekten zurück. Die Sequenz enthält null oder mehr Elemente, die dem Schlüsselwert für die Gruppe entsprechen. Sie können z.B. eine Sequenz von Zeichenfolgen entsprechend des ersten Buchstaben in jeder Zeichenfolge gruppieren. In diesem Fall ist der erste Buchstabe der Schlüssel und hat ein Typzeichen. Jedes IGrouping<TKey,TElement> Objekt speichert diesen Schlüssel in seiner Key Eigenschaft. Der Compiler leitet den Typ des Schlüssels her.
Die C#-Sprachreferenz dokumentiert die zuletzt veröffentlichte Version der C#-Sprache. Außerdem enthält sie eine erste Dokumentation zu Funktionen in der öffentlichen Vorschau für die kommende Sprachversion.
In der Dokumentation werden alle Features identifiziert, die in den letzten drei Versionen der Sprache oder in der aktuellen öffentlichen Vorschau eingeführt wurden.
Tipp
Informationen dazu, wann ein Feature erstmals in C# eingeführt wurde, finden Sie im Artikel zum Versionsverlauf der C#-Sprache.
Sie können einen Abfrageausdruck mit einer group-Klausel beenden, so wie in folgendem Beispiel gezeigt:
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
from student in students
group student by student.Last[0];
Wenn Sie zusätzliche Abfragevorgänge für jede Gruppe ausführen möchten, geben Sie einen temporären Bezeichner mithilfe des kontextbezogenen Schlüsselworts an. Wenn Sie into verwenden, müssen Sie mit der Abfrage fortfahren und sie entweder mit einer select-Anweisung oder einer anderen group-Klausel beenden, so wie im folgenden Auszug dargestellt:
// Group students by the first letter of their last name
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery2 =
from student in students
group student by student.Last[0] into g
orderby g.Key
select g;
Weitere vollständige Gebrauchsbeispiele für group mit und ohne into sind im Abschnitt über Beispiele in diesem Artikel enthalten.
Auflisten der Ergebnisse einer Gruppenabfrage
Da die IGrouping<TKey,TElement> von einer group Abfrage erzeugten Objekte im Wesentlichen eine Liste von Listen sind, müssen Sie eine geschachtelte Foreachschleife verwenden, um auf die Elemente in jeder Gruppe zuzugreifen. Die äußere Schleife durchläuft die Gruppenschlüssel, und die innere Schleife durchläuft jedes Element in der Gruppe selbst. Eine Gruppe kann über einen Schlüssel verfügen, aber keine Elemente. Die folgende foreach Schleife führt die Abfrage in den vorherigen Codebeispielen aus:
// Iterate group items with a nested foreach. This IGrouping encapsulates
// a sequence of Student objects, and a Key of type char.
// For convenience, var can also be used in the foreach statement.
foreach (IGrouping<char, Student> studentGroup in studentQuery2)
{
Console.WriteLine(studentGroup.Key);
// Explicit type for student could also be used here.
foreach (var student in studentGroup)
{
Console.WriteLine(" {0}, {1}", student.Last, student.First);
}
}
Schlüsseltypen
Gruppenschlüssel können von jedem Typ sein, z.B. eine Zeichenfolge, ein integrierter numerischer Typ oder ein benutzerdefinierter benannter oder anonymer Typ.
Gruppieren nach Zeichenkette
Das vorherige Codebeispiel verwendete einen char. Sie können auch einen Zeichenfolgenschlüssel angeben, z. B. den vollständigen Nachnamen:
// Same as previous example except we use the entire last name as a key.
// Query variable is an IEnumerable<IGrouping<string, Student>>
var studentQuery3 =
from student in students
group student by student.Last;
Gruppieren nach Boolean
Im folgenden Beispiel wird ein Boolwert für einen Schlüssel verwendet, um die Ergebnisse in zwei Gruppen aufzuteilen. Der Wert stammt aus einem Unterausdruck in der group Klausel.
class GroupSample1
{
// The element type of the data source.
public class Student
{
public required string First { get; init; }
public required string Last { get; init; }
public required int ID { get; init; }
public required List<int> Scores;
}
public static List<Student> GetStudents()
{
// Use a collection initializer to create the data source. Note that each element
// in the list contains an inner sequence of scores.
List<Student> students =
[
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= [97, 72, 81, 60]},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= [75, 84, 91, 39]},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= [99, 89, 91, 95]},
new Student {First="Cesar", Last="Garcia", ID=114, Scores= [72, 81, 65, 84]},
new Student {First="Debra", Last="Garcia", ID=115, Scores= [97, 89, 85, 82]}
];
return students;
}
static void Main()
{
// Obtain the data source.
List<Student> students = GetStudents();
// Group by true or false.
// Query variable is an IEnumerable<IGrouping<bool, Student>>
var booleanGroupQuery =
from student in students
group student by student.Scores.Average() >= 80; //pass or fail!
// Execute the query and access items in each group
foreach (var studentGroup in booleanGroupQuery)
{
Console.WriteLine(studentGroup.Key == true ? "High averages" : "Low averages");
foreach (var student in studentGroup)
{
Console.WriteLine(" {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
}
}
}
}
/* Output:
Low averages
Omelchenko, Svetlana:77.5
O'Donnell, Claire:72.25
Garcia, Cesar:75.5
High averages
Mortensen, Sven:93.5
Garcia, Debra:88.25
*/
Gruppieren nach numerischen Bereich
Das nächste Beispiel verwendet einen Ausdruck, um einen nummerischen Gruppenschlüssel zu erstellen, der einen Prozentbereich darstellt. Es wird let verwendet, um ein Methodenaufrufergebnis zu speichern, sodass Sie die Methode nicht zweimal in der group Klausel aufrufen müssen. Weitere Informationen zur sicheren Verwendung von Methoden in Abfrageausdrücken finden Sie unter Behandeln von Ausnahmen in Abfrageausdrücken.
class GroupSample2
{
// The element type of the data source.
public class Student
{
public required string First { get; init; }
public required string Last { get; init; }
public required int ID { get; init; }
public required List<int> Scores;
}
public static List<Student> GetStudents()
{
// Use a collection initializer to create the data source. Note that each element
// in the list contains an inner sequence of scores.
List<Student> students =
[
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= [97, 72, 81, 60]},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= [75, 84, 91, 39]},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= [99, 89, 91, 95]},
new Student {First="Cesar", Last="Garcia", ID=114, Scores= [72, 81, 65, 84]},
new Student {First="Debra", Last="Garcia", ID=115, Scores= [97, 89, 85, 82]}
];
return students;
}
// This method groups students into percentile ranges based on their
// grade average. The Average method returns a double, so to produce a whole
// number it is necessary to cast to int before dividing by 10.
static void Main()
{
// Obtain the data source.
List<Student> students = GetStudents();
// Write the query.
var studentQuery =
from student in students
let avg = (int)student.Scores.Average()
group student by (avg / 10) into g
orderby g.Key
select g;
// Execute the query.
foreach (var studentGroup in studentQuery)
{
int temp = studentGroup.Key * 10;
Console.WriteLine($"Students with an average between {temp} and {temp + 10}");
foreach (var student in studentGroup)
{
Console.WriteLine(" {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
}
}
}
}
/* Output:
Students with an average between 70 and 80
Omelchenko, Svetlana:77.5
O'Donnell, Claire:72.25
Garcia, Cesar:75.5
Students with an average between 80 and 90
Garcia, Debra:88.25
Students with an average between 90 and 100
Mortensen, Sven:93.5
*/
Gruppieren nach zusammengesetzten Schlüsseln
Verwenden Sie einen zusammengesetzten Schlüssel, wenn Sie Elemente nach mehreren Schlüsseln gruppieren möchten. Erstellen Sie einen zusammengesetzten Schlüssel mithilfe eines anonymen Typs oder eines benannten Typs, um das Schlüsselelement aufzunehmen. Gehen Sie im folgenden Beispiel davon aus, dass eine Klasse Person Member mit dem Namen surname und dem Namen hat city. Die group Klausel erstellt eine separate Gruppe für jede Gruppe von Personen mit demselben Nachnamen und derselben Stadt.
group person by new {name = person.surname, city = person.city};
Verwenden Sie einen benannten Typ, wenn Sie die Abfragevariable an eine andere Methode übergeben müssen. Erstellen Sie eine spezielle Klasse mit automatisch implementierten Eigenschaften für die Schlüssel, und überschreiben Sie dann die und GetHashCode die Equals Methoden. Sie können auch eine Struktur verwenden, die diese Methodenüberschreibungen nicht unbedingt erfordert. Weitere Informationen finden Sie unter Implementieren einer einfachen Klasse mit automatisch implementierten Eigenschaften und Abfragen von duplizierten Dateien in einer Verzeichnisstruktur. Der zweite Artikel enthält ein Codebeispiel, in dem dargestellt wird, wie ein zusammengesetzter Schlüssel mit einem benannten Typ verwendet wird.
Examples
Das folgende Beispiel zeigt das Standardmuster zum Sortieren von Quelldaten in Gruppen, wenn Sie keine zusätzliche Abfragelogik auf die Gruppen anwenden. Dieses Muster wird als Gruppierung ohne Fortsetzung bezeichnet. Im Beispiel werden die Elemente in einem Array von Zeichenfolgen gemäß ihrem ersten Buchstaben gruppiert. Das Ergebnis der Abfrage ist ein IGrouping<TKey,TElement>-Typ, der eine öffentliche Key-Eigenschaft des Typs char und eine IEnumerable<T>-Sammlung enthält, die jedes Element in der Gruppierung enthält.
Das Ergebnis einer group-Klausel ist eine Sequenz von Sequenzen. Um auf die einzelnen Elemente in jeder zurückgegebenen Gruppe zuzugreifen, verwenden Sie eine geschachtelte foreach Schleife in der Schleife, die die Gruppenschlüssel durchläuft, wie im folgenden Beispiel gezeigt.
class GroupExample1
{
static void Main()
{
// Create a data source.
string[] words = ["blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese"];
// Create the query.
var wordGroups =
from w in words
group w by w[0];
// Execute the query.
foreach (var wordGroup in wordGroups)
{
Console.WriteLine($"Words that start with the letter '{wordGroup.Key}':");
foreach (var word in wordGroup)
{
Console.WriteLine(word);
}
}
}
}
/* Output:
Words that start with the letter 'b':
blueberry
banana
Words that start with the letter 'c':
chimpanzee
cheese
Words that start with the letter 'a':
abacus
apple
*/
Das folgende Beispiel zeigt, wie Sie zusätzliche Logik für die Gruppen ausführen, nachdem Sie sie erstellt haben, mithilfe einer Fortsetzung mit into. Weitere Informationen finden Sie unter into. Das folgende Beispiel fragt jede Gruppe ab, wobei nur die Gruppe ausgewählt werden soll, dessen Schlüsselwert ein Vokal ist.
class GroupClauseExample2
{
static void Main()
{
// Create the data source.
string[] words2 = ["blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese", "elephant", "umbrella", "anteater"];
// Create the query.
var wordGroups2 =
from w in words2
group w by w[0] into grps
where (grps.Key == 'a' || grps.Key == 'e' || grps.Key == 'i'
|| grps.Key == 'o' || grps.Key == 'u')
select grps;
// Execute the query.
foreach (var wordGroup in wordGroups2)
{
Console.WriteLine($"Groups that start with a vowel: {wordGroup.Key}");
foreach (var word in wordGroup)
{
Console.WriteLine($" {word}");
}
}
}
}
/* Output:
Groups that start with a vowel: a
abacus
apple
anteater
Groups that start with a vowel: e
elephant
Groups that start with a vowel: u
umbrella
*/
Zur Kompilierungszeit übersetzt group der Compiler Klauseln in Aufrufe der GroupBy Methode.
Die Syntax der group Klauselabfrage unterstützt keinen benutzerdefinierten Gleichheitsvergleich. Wenn Sie in Ihrer Abfrage verwenden IEqualityComparer möchten, verwenden Sie die GroupBy Methode explizit.