C#-Funktionen mit LINQ-Unterstützung

Abfrageausdrücke

Abfrageausdrücke verwenden eine deklarative Syntax wie SQL oder XQuery, um eine Abfrage über System.Collections.Generic.IEnumerable<T>-Sammlungen zu erstellen. Zur Kompilierzeit wird die Abfragesyntax in Methodenaufrufe in eine Implementierung des LINQ-Anbieters der Standardabfragemethoden konvertiert. Applikationen steuern die Standardabfrageoperatoren, die sich durch Angabe des entsprechenden Namespace mit einer using-Anweisung innerhalb des Bereichs befinden. Der folgende Abfrageausdruck nimmt ein Array von Zeichenfolgen, gruppiert sie nach dem ersten Zeichen in der Zeichenfolge und sortiert die Gruppen.

var query = from str in stringArray
            group str by str[0] into stringGroup
            orderby stringGroup.Key
            select stringGroup;

Implizit typisierte Variablen (var)

Sie können den var-Modifizierer verwenden, um den Compiler zum Ableiten und Zuweisen des Typs anzuweisen, wie hier gezeigt:

var number = 5;
var name = "Virginia";
var query = from str in stringArray
            where str[0] == 'm'
            select str;

Variablen, die als var deklariert werden, sind ebenso stark typisiert wie Variablen, deren Typ Sie explizit angeben. Die Verwendung von var macht es möglich, anonyme Typen zu erstellen, jedoch nur für lokale Variablen. Weitere Informationen finden Sie unter Implizit typisierte lokale Variablen.

Objekt- und Auflistungsinitialisierer

Objekt- und Auflistungsinitialisierer ermöglichen das Initialisieren von Objekten ohne expliziten Aufruf eines Konstruktors für das Objekt. Initialisierer werden in der Regel in Abfrageausdrücken verwendet, wenn sie die Quelldaten in einen neuen Datentyp projizieren. In einer Klasse namens Customer mit öffentlichen Name- und Phone-Eigenschaften können Objektinitialisierer wie im folgenden Code verwendet werden:

var cust = new Customer { Name = "Mike", Phone = "555-1212" };

Wenn Sie mit der Customer-Klasse fortfahren, gehen Sie davon aus, dass eine Datenquelle namens IncomingOrders vorhanden ist und dass für jede Bestellung mit einem großen OrderSize-Wert ein neues Customer-Element basierend auf dieser Bestellung erstellt werden soll. Eine LINQ-Abfrage kann auf dieser Datenquelle ausgeführt werden und verwendet die Objektinitialisierung, um eine Auflistung zu füllen:

var newLargeOrderCustomers = from o in IncomingOrders
                            where o.OrderSize > 5
                            select new Customer { Name = o.Name, Phone = o.Phone };

Für die Datenquelle können mehr Eigenschaften definiert sein als für die Klasse Customer, z. B. OrderSize, aber bei der Objektinitialisierung werden die von der Abfrage zurückgegebenen Daten in den gewünschten Datentyp umgewandelt. Wählen Sie die Daten aus, die für Ihre Klasse relevant sind. Daher verfügen Sie jetzt über ein System.Collections.Generic.IEnumerable<T>-Element, das mit den neuen gewünschten Customer-Informationen gefüllt ist. Das obige Beispiel kann auch in der Methodensyntax von LINQ geschrieben werden:

var newLargeOrderCustomers = IncomingOrders.Where(x => x.OrderSize > 5).Select(y => new Customer { Name = y.Name, Phone = y.Phone });

Ab C# 12 können Sie einen Sammlungsausdruck verwenden, um eine Sammlung zu initialisieren.

Weitere Informationen finden Sie unter:

Anonyme Typen

Der Compiler erstellt einen anonymen Typ. Der Typname ist nur für den Compiler verfügbar. Anonyme Typen stellen eine bequeme Möglichkeit zum vorübergehenden Gruppieren einer Reihe von Eigenschaften in einem Abfrageergebnis bereit, ohne einen separaten benannten Typ definieren zu müssen. Anonyme Typen werden mit einem neuen Ausdruck und einem Objektinitialisierer initialisiert, wie hier gezeigt:

select new {name = cust.Name, phone = cust.Phone};

Ab C# 7 können Sie Tupel verwenden, um unbenannte Typen zu erstellen.

Erweiterungsmethoden

Eine Erweiterungsmethode ist eine statische Methode, die einem Typ zugeordnet werden kann, sodass sie aufgerufen werden kann, als ob es sich um eine Instanzmethode für den Typ handeln würde. Diese Funktion ermöglicht es Ihnen, neue Methoden zu vorhandenen Typen „hinzuzufügen“, ohne sie tatsächlich zu ändern. Die Standardabfrageoperatoren sind eine Reihe von Erweiterungsmethoden, die LINQ-Abfragefunktionen für jeden Typ bieten, der IEnumerable<T> implementiert.

Lambda-Ausdrücke

Ein Lambdaausdruck ist eine Inlinefunktion, die den Operator => verwendet, um Eingabeparameter vom Funktionstext zu trennen, und die zur Kompilierzeit in einen Delegaten oder eine Ausdrucksbaumstruktur konvertiert werden kann. In der LINQ-Programmierung erhalten Sie Lambdaausdrücke, wenn Sie direkte Methodenaufrufe für die Standardabfrageoperatoren vornehmen.

Ausdrücke als Daten

Abfrageobjekte sind zusammensetzbar, das bedeutet, dass Sie eine Abfrage aus einer Methode zurückgeben können. Objekte, die Abfragen darstellen, speichern nicht die resultierende Auflistung, sondern bei Bedarf die Schritte zum Erzeugen der Ergebnisse. Der Vorteil des Zurückgebens von Abfrageobjekten aus Methoden ist, dass diese weiter zusammengesetzt oder geändert werden können. Daher muss ein Rückgabewert oder ein out-Parameter einer Methode, die eine Abfrage zurückgibt, über diesen Typ verfügen. Wenn eine Methode eine Abfrage in den konkreten Typ List<T> oder Array materialisiert, gibt sie die Abfrageergebnisse zurück, nicht die Abfrage selbst. Eine Abfragevariable, die aus einer Methode zurückgegeben wird, kann noch zusammengesetzt oder geändert werden.

Im folgenden Beispiel gibt die erste Methode QueryMethod1 eine Abfrage als Rückgabewert zurück. Die zweite Methode QueryMethod2 gibt eine Abfrage als out-Parameter (returnQ im Beispiel) zurück. In beiden Fällen handelt es sich um eine Abfrage, die zurückgegeben wird, nicht um Abfrageergebnisse.

IEnumerable<string> QueryMethod1(int[] ints) =>
    from i in ints
    where i > 4
    select i.ToString();

void QueryMethod2(int[] ints, out IEnumerable<string> returnQ) =>
    returnQ =
        from i in ints
        where i < 4
        select i.ToString();

int[] nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var myQuery1 = QueryMethod1(nums);

Abfrage myQuery1 wird in der folgenden Foreachschleife ausgeführt.

foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}

Positionieren Sie den Mauszeiger über myQuery1, um den Typ anzuzeigen.

Sie können auch die von QueryMethod1 zurückgegebene Abfrage direkt ausführen, ohne myQuery1 zu verwenden.

foreach (var s in QueryMethod1(nums))
{
    Console.WriteLine(s);
}

Positionieren Sie den Mauszeiger über dem Aufruf an QueryMethod1, um den Rückgabetyp anzuzeigen.

QueryMethod2 gibt eine Abfrage als Wert ihres out-Parameters zurück:

QueryMethod2(nums, out IEnumerable<string> myQuery2);

// Execute the returned query.
foreach (var s in myQuery2)
{
    Console.WriteLine(s);
}

Sie können eine Abfrage mithilfe der Abfragekomposition ändern. In diesem Fall wird das vorherige Abfrageobjekt verwendet, um ein neues Abfrageobjekt zu erstellen. Dieses neue Objekt gibt andere Ergebnisse als das ursprüngliche Abfrageobjekt zurück.

myQuery1 =
    from item in myQuery1
    orderby item descending
    select item;

// Execute the modified query.
Console.WriteLine("\nResults of executing modified myQuery1:");
foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}