Psaní dotazů LINQ jazyka C# pro dotazování na data

Většina dotazů v úvodní dokumentaci k integrovanému dotazu jazyka (LINQ) je napsaná pomocí deklarativní syntaxe dotazů LINQ. Syntaxe dotazu však musí být při kompilaci kódu přeložena do volání metod modulu CLR (Common Language Runtime) .NET. Tato metoda volá volání standardních operátorů dotazu, které mají názvy, jako Wherejsou , Select, GroupBy, Join, Maxa Average. Můžete je volat přímo pomocí syntaxe metody místo syntaxe dotazu.

Syntaxe dotazů a syntaxe metody jsou sémanticky identické, ale syntaxe dotazu je často jednodušší a čitelnější. Některé dotazy musí být vyjádřeny jako volání metody. Například je nutné použít volání metody k vyjádření dotazu, který načte počet prvků, které odpovídají zadané podmínce. Musíte také použít volání metody pro dotaz, který načte prvek, který má maximální hodnotu ve zdrojové sekvenci. Referenční dokumentace pro standardní operátory dotazů v System.Linq oboru názvů obecně používá syntaxi metody. Měli byste se seznámit s používáním syntaxe metod v dotazech a v samotných výrazech dotazů.

Metody rozšíření standardního operátoru dotazu

Následující příklad ukazuje jednoduchý výraz dotazu a sémanticky ekvivalentní dotaz napsaný jako dotaz založený na metodě.

int[] numbers = [ 5, 10, 8, 3, 6, 12 ];

//Query syntax:
IEnumerable<int> numQuery1 =
    from num in numbers
    where num % 2 == 0
    orderby num
    select num;

//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

foreach (int i in numQuery1)
{
    Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
    Console.Write(i + " ");
}

Výstup z těchto dvou příkladů je shodný. Vidíte, že typ proměnné dotazu je stejný v obou formách: IEnumerable<T>.

Abychom pochopili dotaz založený na metodách, pojďme ho podrobněji prozkoumat. Na pravé straně výrazu si všimněte, že where klauzule je nyní vyjádřena jako metoda instance objektu numbers , který má typ IEnumerable<int>. Pokud znáte obecné IEnumerable<T> rozhraní, víte, že nemá metodu Where . Pokud však vyvoláte seznam dokončování IntelliSense v integrovaném vývojovém prostředí sady Visual Studio, uvidíte nejen metodu Where , ale mnoho dalších metod, jako Selectje , SelectMany, Joina Orderby. Tyto metody implementují standardní operátory dotazu.

Screenshot showing all the standard query operators in Intellisense.

I když to vypadá, jako by IEnumerable<T> obsahovalo další metody, tak to není. Standardní operátory dotazů se implementují jako rozšiřující metody. Metody rozšíření "rozšiřují" existující typ; lze je volat, jako by se jednalo o metody instance typu. Standardní operátory dotazu rozšiřují IEnumerable<T> a proto můžete psát numbers.Where(...).

Pokud chcete použít rozšiřující metody, přenesete je do oboru direktivami using . Z pohledu vaší aplikace je metoda rozšíření a běžná metoda instance stejné.

Další informace o metodách rozšíření naleznete v tématu Metody rozšíření. Další informace o standardních operátorech dotazů najdete v tématu Přehled operátorů standardních dotazů (C#). Někteří poskytovatelé LINQ, jako je Entity Framework a LINQ to XML, implementují vlastní standardní operátory dotazů a rozšiřující metody pro jiné typy kromě IEnumerable<T>.

Výrazy lambda

V předchozím příkladu si všimněte, že podmíněný výraz (num % 2 == 0) je předán jako vložený argument Enumerable.Where metodě: Where(num => num % 2 == 0). Tento vložený výraz je výraz lambda. Je to pohodlný způsob, jak napsat kód, který by jinak musel být napsán v složitější podobě. Nalevo num od operátoru je vstupní proměnná, která odpovídá num ve výrazu dotazu. Kompilátor může odvodit typ, num protože ví, že numbers je to obecný IEnumerable<T> typ. Tělo výrazu lambda je stejné jako výraz v syntaxi dotazu nebo v jakémkoli jiném výrazu nebo příkazu jazyka C#. Může zahrnovat volání metod a další složitou logiku. Vrácená hodnota je pouze výsledek výrazu. Některé dotazy lze vyjádřit pouze v syntaxi metody a některé z nich vyžadují výrazy lambda. Výrazy lambda jsou výkonným a flexibilním nástrojem v sadě nástrojů LINQ.

Kompozičnost dotazů

V předchozím příkladu Enumerable.OrderBy kódu je metoda vyvolána pomocí tečkového operátoru volání Where. Wherevytvoří filtrovanou sekvenci a potom Orderby seřadí sekvenci vytvořenou .Where Vzhledem k tomu, že dotazy vrací IEnumerable, vytvoříte je v syntaxi metody zřetězeným volání metody. Kompilátor toto složení provede při psaní dotazů pomocí syntaxe dotazu. Vzhledem k tomu, že proměnná dotazu neukládá výsledky dotazu, můžete ji kdykoli upravit nebo použít jako základ pro nový dotaz, a to i po jeho spuštění.

Následující příklady demonstrují některé jednoduché dotazy LINQ pomocí každého dříve uvedeného přístupu.

Poznámka:

Tyto dotazy pracují s jednoduchými kolekcemi v paměti; Základní syntaxe je však shodná s syntaxí, která se používá v LINQ to Entities a LINQ to XML.

Příklad – syntaxe dotazu

Většinu dotazů se syntaxí dotazu napíšete za účelem vytváření výrazů dotazu. Následující příklad ukazuje tři výrazy dotazu. První výraz dotazu ukazuje, jak filtrovat nebo omezit výsledky použitím podmínek s klauzulí where . Vrátí všechny prvky ve zdrojové sekvenci, jejichž hodnoty jsou větší než 7 nebo menší než 3. Druhý výraz ukazuje, jak uspořádat vrácené výsledky. Třetí výraz ukazuje, jak seskupit výsledky podle klíče. Tento dotaz vrátí dvě skupiny založené na prvním písmenu slova.

List<int> numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];

// The query variables can also be implicitly typed by using var

// Query #1.
IEnumerable<int> filteringQuery =
    from num in numbers
    where num is < 3 or > 7
    select num;

// Query #2.
IEnumerable<int> orderingQuery =
    from num in numbers
    where num is < 3 or > 7
    orderby num ascending
    select num;

// Query #3.
string[] groupingQuery = ["carrots", "cabbage", "broccoli", "beans", "barley"];
IEnumerable<IGrouping<char, string>> queryFoodGroups =
    from item in groupingQuery
    group item by item[0];

Typ dotazů je IEnumerable<T>. Všechny tyto dotazy lze napsat pomocí následujícího var příkladu:

var query = from num in numbers...

V každém předchozím příkladu se dotazy ve skutečnosti neprovedou, dokud ne iterujete proměnnou foreach dotazu v příkazu nebo jiném příkazu.

Příklad – syntaxe metody

Některé operace dotazu musí být vyjádřeny jako volání metody. Nejběžnějšími metodami jsou metody, které vracejí jednoúčelové číselné hodnoty, například Sum, Max, Min, Averageatd. Tyto metody musí být vždy volána jako poslední v jakémkoli dotazu, protože vracejí jednu hodnotu a nemůžou sloužit jako zdroj pro další operaci dotazu. Následující příklad ukazuje volání metody ve výrazu dotazu:

List<int> numbers1 = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
List<int> numbers2 = [15, 14, 11, 13, 19, 18, 16, 17, 12, 10];

// Query #4.
double average = numbers1.Average();

// Query #5.
IEnumerable<int> concatenationQuery = numbers1.Concat(numbers2);

Pokud metoda má System.Action nebo System.Func<TResult> parametry, jsou tyto argumenty uvedeny ve formě výrazu lambda, jak je znázorněno v následujícím příkladu:

// Query #6.
IEnumerable<int> largeNumbersQuery = numbers2.Where(c => c > 15);

V předchozích dotazech se provede okamžitě pouze dotaz č. 4, protože vrátí jednu hodnotu, a ne obecnou IEnumerable<T> kolekci. Samotná metoda používá foreach nebo podobný kód k výpočtu jeho hodnoty.

Každý z předchozích dotazů lze zapsat pomocí implicitního psaní s výrazem var, jak je znázorněno v následujícím příkladu:

// var is used for convenience in these queries
double average = numbers1.Average();
var concatenationQuery = numbers1.Concat(numbers2);
var largeNumbersQuery = numbers2.Where(c => c > 15);

Příklad – syntaxe smíšených dotazů a metod

Tento příklad ukazuje, jak používat syntaxi metody pro výsledky klauzule dotazu. Stačí uzavřít výraz dotazu do závorek a pak použít tečkovací operátor a volat metodu. V následujícím příkladu vrátí dotaz č. 7 počet čísel, jejichž hodnota je mezi 3 a 7. Obecně je ale lepší použít druhou proměnnou k uložení výsledku volání metody. Tímto způsobem je méně pravděpodobné, že se dotaz zaměňuje s výsledky dotazu.

// Query #7.

// Using a query expression with method syntax
var numCount1 = (
    from num in numbers1
    where num is > 3 and < 7
    select num
).Count();

// Better: Create a new variable to store
// the method call result
IEnumerable<int> numbersQuery =
    from num in numbers1
    where num is > 3 and < 7
    select num;

var numCount2 = numbersQuery.Count();

Vzhledem k tomu, že dotaz č. 7 vrátí jednu hodnotu, a ne kolekci, spustí se dotaz okamžitě.

Předchozí dotaz lze zapsat pomocí implicitního psaní varnásledujícím způsobem:

var numCount = (from num in numbers...

Dá se napsat v syntaxi metody následujícím způsobem:

var numCount = numbers.Count(n => n is > 3 and < 7);

Lze ho napsat pomocí explicitního psaní následujícím způsobem:

int numCount = numbers.Count(n => n is > 3 and < 7);

Viz také