オブジェクトのコレクションの照会

"LINQ to Objects" という用語は、LINQ to SQLLINQ to XML などの中間 LINQ プロバイダーまたは API を使用せずに、LINQ クエリを任意の IEnumerable コレクションまたは IEnumerable<T> コレクションと直接組み合わせて使用することを意味します。 LINQ を使用して、List<T>ArrayDictionary<TKey,TValue> などの任意の列挙可能なコレクションを照会できます。 このコレクションは、ユーザー定義のコレクションでも、.NET API から返されたコレクションでもかまいません。 LINQ を使用する場合は、何を取得するかを表す宣言コードを記述します。

また、LINQ クエリには、従来の foreach ループと比べて、次の 3 つの重要な利点があります。

  • 簡潔で読みやすい (特に複数の条件をフィルター処理する場合)。
  • 強力なフィルター処理、並べ替え、およびグループ化機能を最小限のアプリケーション コードで実現できる。
  • ほとんど、またはまったく変更せずに、他のデータ ソースに移植できる。

通常、データに対して実行する操作が複雑なほど、従来の反復処理手法の代わりに LINQ を使用する利便性が高くなります。

この例では、Student オブジェクトのリストに対してシンプルなクエリを実行する方法を示します。 各 Student オブジェクトには、生徒に関する基本情報と、4 回の試験での生徒の点数を表すリストが含まれています。

注意

このセクションに示す他の例の多くで、同じ Student クラスおよび students コレクションが使用されています。

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
};

次のクエリは、最初の試験で 90 点以上を取った学生を返します。

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);

このクエリは、実験用として意図的にシンプルに記述されています。 たとえば、where 句でより多く条件を試したり、orderby 句を使用して結果を並べ替えたりすることもできます。

実行方法による標準クエリ演算子の分類

標準クエリ演算子メソッドの LINQ to Objects 実装は、主に 2 とおりの方法 (即時と遅延) で実行されます。 遅延実行を使用するクエリ演算子は、さらに 2 つのカテゴリ (ストリーミングと非ストリーミング) に分けることができます。 それぞれのクエリ演算子がどのように動作するかを把握しておくと、指定したクエリの結果を理解するうえで役立ちます。 これは、データ ソースが変更される場合や、別のクエリに基づいてさらにクエリを作成する場合に特に便利です。 このトピックでは、標準クエリ演算子を、その実行方法に基づいて分類します。

イミディエイト

即時実行とは、データ ソースが読み取られ、演算が 1 回実行されることを意味します。 スカラー結果を返す標準クエリ演算子はすべて即時に実行されます。 Enumerable.ToList または Enumerable.ToArray メソッドを使用すると、クエリを即時実行するように強制できます。 即時実行では、クエリ宣言ではなく、クエリ結果を再利用できます。 結果は 1 回取得され、後で使用するために格納されます。

遅延

遅延実行とは、クエリが宣言されたコード内の位置では演算が実行されないことを意味します。 演算は、foreach ステートメントを使用するなどの方法により、クエリ変数が列挙されたときにのみ実行されます。 つまり、クエリの実行結果は、クエリの定義時ではなくクエリの実行時のデータ ソースの内容に依存します。 クエリ変数が複数回列挙される場合は、そのたびに結果が変わる可能性があります。 戻り値の型が IEnumerable<T> または IOrderedEnumerable<TElement> の標準クエリ演算子は、ほとんどが遅延実行されます。 遅延実行では、クエリ結果が反復されるたびに、クエリでデータ ソースから更新されたデータがフェッチされるため、クエリの再利用機能が提供されます。

遅延実行を使用するクエリ演算子は、さらにストリーミングか非ストリーミングに分類できます。

ストリーム

ストリーミング演算子では、要素を生成する前にすべてのソース データを読み取る必要はありません。 実行時に、ストリーミング演算子は読み取ったソース要素ごとに演算を実行し、必要に応じて要素を生成します。 ストリーミング演算子は、結果の要素を生成できるまでソース要素の読み取りを続行します。 つまり、結果の要素を 1 つ生成するために複数のソース要素が読み取られる場合があります。

非ストリーミング

非ストリーミング演算子では、結果の要素を生成する前にすべてのソース データを読み取る必要があります。 並べ替えやグループ化などの演算はこのカテゴリに分類されます。 実行時に、非ストリーミング クエリ演算子はすべてのソース データを読み取ってデータ構造に格納し、演算を実行して結果の要素を生成します。

分類表

次の表では、各標準クエリ演算子メソッドを、その実行方法に基づいて分類しています。

注意

2 つの列にマークが付けられている演算子では、2 つの入力シーケンスが演算に使用され、各シーケンスの評価は異なります。 この場合、遅延実行のストリーミングで評価されるのは、常にパラメーター リストの最初のシーケンスになります。

標準クエリ演算子 の戻り値の型 : 即時実行 遅延実行 (ストリーミング) 遅延実行 (非ストリーミング)
Aggregate TSource X
All Boolean X
Any Boolean X
AsEnumerable IEnumerable<T> X
Average 1 つの数値 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 単一数値、 TSource または TResult? X
Min 単一数値、 TSource または 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 1 つの数値 x
Take IEnumerable<T> X
TakeWhile IEnumerable<T> X
ThenBy IOrderedEnumerable<TElement> X
ThenByDescending IOrderedEnumerable<TElement> X
ToArray TSource[] 配列 X
ToDictionary Dictionary<TKey,TValue> X
ToList IList<T> X
ToLookup ILookup<TKey,TElement> X
Union IEnumerable<T> X
Where IEnumerable<T> X

関連項目