Dotazování na kolekci objektů

Termín "LINQ to Objects" odkazuje na použití dotazů LINQ s libovolnou IEnumerable nebo IEnumerable<T> kolekcí přímo bez použití zprostředkujícího zprostředkovatele LINQ nebo rozhraní API, jako je LINQ to SQL nebo LINQ to XML. LinQ můžete použít k dotazování na všechny výčtové kolekce, jako List<T>je , Arraynebo Dictionary<TKey,TValue>. Kolekce může být definovaná uživatelem nebo může být vrácena rozhraním .NET API. V přístupu LINQ napíšete deklarativní kód, který popisuje, co chcete načíst.

Kromě toho dotazy LINQ nabízejí tři hlavní výhody oproti tradičním foreach smyčkám:

  • Jsou stručnější a čitelnější, zejména při filtrování více podmínek.
  • Poskytují výkonné možnosti filtrování, řazení a seskupování s minimálním kódem aplikace.
  • Mohou být portovány do jiných zdrojů dat s malými nebo žádnými úpravami.

Obecně platí, že čím složitější je operace, kterou chcete s daty provádět, tím větší výhodou je použití LINQ místo tradičních iteračních technik.

Tento příklad ukazuje, jak provést jednoduchý dotaz na seznam Student objektů. Každý Student objekt obsahuje některé základní informace o studentovi a seznam, který představuje skóre studenta na čtyřech zkouškách.

Poznámka:

Mnoho dalších příkladů v této části používá stejnou Student třídu a students kolekci.

class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int ID { get; set; }
    public GradeLevel? Year { get; set; }
    public List<int> ExamScores { get; set; }

    public Student(string FirstName, string LastName, int ID, GradeLevel Year, List<int> ExamScores)
    {
        this.FirstName = FirstName;
        this.LastName = LastName;
        this.ID = ID;
        this.Year = Year;
        this.ExamScores = ExamScores;
    }

    public Student(string FirstName, string LastName, int StudentID, List<int>? ExamScores = null)
    {
        this.FirstName = FirstName;
        this.LastName = LastName;
        ID = StudentID;
        this.ExamScores = ExamScores ?? [];
    }

    public static List<Student> students =
    [
        new(
            FirstName: "Terry", LastName: "Adams", ID: 120,
            Year: GradeLevel.SecondYear,
            ExamScores: [99, 82, 81, 79]
        ),
        new(
            "Fadi", "Fakhouri", 116,
            GradeLevel.ThirdYear,
            [99, 86, 90, 94]
        ),
        new(
            "Hanying", "Feng", 117,
            GradeLevel.FirstYear,
            [93, 92, 80, 87]
        ),
        new(
            "Cesar", "Garcia", 114,
            GradeLevel.FourthYear,
            [97, 89, 85, 82]
        ),
        new(
            "Debra", "Garcia", 115,
            GradeLevel.ThirdYear,
            [35, 72, 91, 70]
        ),
        new(
            "Hugo", "Garcia", 118,
            GradeLevel.SecondYear,
            [92, 90, 83, 78]
        ),
        new(
            "Sven", "Mortensen", 113,
            GradeLevel.FirstYear,
            [88, 94, 65, 91]
        ),
        new(
            "Claire", "O'Donnell", 112,
            GradeLevel.FourthYear,
            [75, 84, 91, 39]
        ),
        new(
            "Svetlana", "Omelchenko", 111,
            GradeLevel.SecondYear,
            [97, 92, 81, 60]
        ),
        new(
            "Lance", "Tucker", 119,
            GradeLevel.ThirdYear,
            [68, 79, 88, 92]
        ),
        new(
            "Michael", "Tucker", 122,
            GradeLevel.FirstYear,
            [94, 92, 91, 91]
        ),
        new(
            "Eugene", "Zabokritski", 121,
            GradeLevel.FourthYear,
            [96, 85, 91, 60]
        )
    ];
}

enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

Příklad

Následující dotaz vrátí studenty, kteří získali při první zkoušce skóre 90 nebo vyšší.

void QueryHighScores(int exam, int score)
{
    var highScores =
        from student in students
        where student.ExamScores[exam] > score
        select new
        {
            Name = student.FirstName,
            Score = student.ExamScores[exam]
        };

    foreach (var item in highScores)
    {
        Console.WriteLine($"{item.Name,-15}{item.Score}");
    }
}

QueryHighScores(0, 90);

Tento dotaz je záměrně jednoduchý, abyste mohli experimentovat. Můžete například vyzkoušet více podmínek v where klauzuli nebo pomocí orderby klauzule seřadit výsledky.

Klasifikace standardních operátorů dotazů způsobem provádění

Implementace LINQ to Objects standardních metod operátoru dotazu se provádějí jedním ze dvou hlavních způsobů: okamžitě nebo odloženo. Operátory dotazů, které používají odložené spouštění, je navíc možné rozdělit do dvou kategorií: streamování a nestreamové streamování. Pokud víte, jak se provádějí různé operátory dotazů, může vám pomoct pochopit výsledky, které získáte z daného dotazu. To platí zejména v případě, že se zdroj dat mění nebo vytváříte dotaz nad jiným dotazem. Toto téma klasifikuje standardní operátory dotazů podle jejich způsobu provádění.

Okamžité

Okamžité spuštění znamená, že je zdroj dat přečtený a operace se provede jednou. Všechny standardní operátory dotazu, které vrací skalární výsledek, se spustí okamžitě. Dotaz můžete vynutit okamžité spuštění pomocí Enumerable.ToList metod nebo Enumerable.ToArray metod. Okamžité spuštění poskytuje opakované použití výsledků dotazu, nikoli deklaraci dotazu. Výsledky se načtou jednou a pak se uloží pro budoucí použití.

Odloženo

Odložené spuštění znamená, že operace není provedena v okamžiku v kódu, ve kterém je dotaz deklarován. Operace se provádí pouze v případě, že je proměnná dotazu vyčíslována, například pomocí foreach příkazu. To znamená, že výsledky provádění dotazu závisejí na obsahu zdroje dat, když se dotaz spustí, a ne při definování dotazu. Pokud je proměnná dotazu vyčíslována vícekrát, výsledky se můžou pokaždé lišit. Téměř všechny standardní operátory dotazu, jejichž návratový typ je IEnumerable<T> nebo IOrderedEnumerable<TElement> spouští odloženým způsobem. Odložené spuštění poskytuje možnost opakovaného použití dotazu, protože dotaz načte aktualizovaná data ze zdroje dat pokaždé, když se výsledky dotazu itehodnotují.

Operátory dotazů, které používají odložené spuštění, je možné dále klasifikovat jako streamování nebo nestreamové streamování.

Streamování

Operátory streamování nemusí před načtením prvků číst všechna zdrojová data. V době provádění operátor streamování provádí svou operaci u každého zdrojového prvku, protože je přečteno a v případě potřeby poskytuje prvek. Operátor streamování nadále čte zdrojové elementy, dokud nebude možné vytvořit výsledný prvek. To znamená, že za účelem vytvoření jednoho výsledného prvku může být přečteno více než jeden zdrojový prvek.

Bez streamování

Operátory bez streamování musí číst všechna zdrojová data, aby mohly přinést výsledný prvek. Operace, jako je řazení nebo seskupení, spadají do této kategorie. V době provádění čtou operátory dotazů bez streamování všechna zdrojová data, umístí je do datové struktury, provede operaci a výslednou část.

Tabulka klasifikace

Následující tabulka klasifikuje každou standardní metodu operátoru dotazu podle metody provádění.

Poznámka:

Pokud je operátor označený ve dvou sloupcích, jsou do operace zapojeny dvě vstupní sekvence a každá sekvence se vyhodnotí jinak. V těchto případech je to vždy první sekvence v seznamu parametrů, která se vyhodnocuje odloženým a streamovaným způsobem.

Standardní operátor dotazu Návratový typ Okamžité spuštění Odložené spuštění streamování Odložené provádění bez streamování
Aggregate TSource X
All Boolean X
Any Boolean X
AsEnumerable IEnumerable<T> X
Average Jedna číselná hodnota X
Cast IEnumerable<T> X
Concat IEnumerable<T> X
Contains Boolean X
Count Int32 X
DefaultIfEmpty IEnumerable<T> X
Distinct IEnumerable<T> X
ElementAt TSource X
ElementAtOrDefault TSource? X
Empty IEnumerable<T> X
Except IEnumerable<T> X X
First TSource X
FirstOrDefault TSource? X
GroupBy IEnumerable<T> X
GroupJoin IEnumerable<T> X X
Intersect IEnumerable<T> X X
Join IEnumerable<T> X X
Last TSource X
LastOrDefault TSource? X
LongCount Int64 X
Max Jedna číselná hodnota, TSourcenebo TResult? X
Min Jedna číselná hodnota, TSourcenebo TResult? X
OfType IEnumerable<T> X
OrderBy IOrderedEnumerable<TElement> X
OrderByDescending IOrderedEnumerable<TElement> X
Range IEnumerable<T> X
Repeat IEnumerable<T> X
Reverse IEnumerable<T> X
Select IEnumerable<T> X
SelectMany IEnumerable<T> X
SequenceEqual Boolean X
Single TSource X
SingleOrDefault TSource? X
Skip IEnumerable<T> X
SkipWhile IEnumerable<T> X
Sum Jedna číselná hodnota X
Take IEnumerable<T> X
TakeWhile IEnumerable<T> X
ThenBy IOrderedEnumerable<TElement> X
ThenByDescending IOrderedEnumerable<TElement> X
ToArray TSource[] Pole X
ToDictionary Dictionary<TKey,TValue> X
ToList IList<T> X
ToLookup ILookup<TKey,TElement> X
Union IEnumerable<T> X
Where IEnumerable<T> X

Viz také