チュートリアル: C# でのクエリの作成 (LINQ)

このチュートリアルでは、LINQ クエリ式の記述に使用される C# 言語の機能について説明します。 このチュートリアルを完了したら、LINQ to SQL、LINQ to DataSets、LINQ to XML など、関心のある特定の LINQ プロバイダーのサンプルやドキュメントに進むことができます。

必須コンポーネント

このチュートリアルでは、Visual Studio 2008 で導入された機能が必要です。

ビデオへのリンク このトピックのビデオ デモについては、「Video How to: Writing Queries in C# (LINQ) (ビデオ デモ: C# でのクエリの作成 (LINQ))」を参照してください。

C# プロジェクトの作成

プロジェクトを作成するには

  1. Visual Studio を起動します。

  2. メニュー バーで [ファイル][新規][プロジェクト] の順にクリックします。

    [新しいプロジェクト] ダイアログ ボックスが表示されます。

  3. [インストール済み] を展開し、[テンプレート] を展開し、[Visual C#] を展開し、を [コンソール アプリケーション] を選択します。

  4. [名前] のテキスト ボックスに、別の名前を入力するか、既定の名前をそのまま使用し、を [OK] のボタンをクリックします。

    ソリューション エクスプローラーに新しいプロジェクトが表示されます。

  5. 作成されたプロジェクトに、System.Core.dll への参照と System.Linq 名前空間に対する using ディレクティブが含まれていることを確認します。

インメモリ データ ソースの作成

クエリのデータ ソースは、Student オブジェクトの単純なリストです。 各 Student レコードには、名、姓、およびクラス内でのその生徒のテストの点数を表す整数の配列が含まれます。 このコードをプロジェクト内にコピーします。 このコードには、次のような特徴があります。

  • Student クラスは、自動実装プロパティで構成されます。

  • リスト内の各生徒は、オブジェクト初期化子によって初期化されます。

  • リスト自体は、コレクション初期化子によって初期化されます。

このデータ構造全体の初期化とインスタンス化は、明示的にコンストラクターを呼び出したり、メンバーにアクセスしたりすることなく行われます。 これらの新しい機能の詳細については、「自動実装するプロパティ (C# プログラミング ガイド)」および「オブジェクト初期化子とコレクション初期化子 (C# プログラミング ガイド)」を参照してください。

データ ソースを追加するには

  • プロジェクトの Program クラスに、Student クラスおよび初期化された生徒のリストを追加します。

    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public List<int> Scores;
    }
    
    // Create a data source by using a collection initializer. 
    static List<Student> students = new List<Student>
    {
       new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
       new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
       new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
       new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
       new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
       new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
       new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
       new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
       new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
       new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
       new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
       new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91} }
    };
    

Students リストに新しい Student を追加するには

  • 新しい Student を Students リストに追加し、任意の名前とテストの点数を指定します。 オブジェクト初期化子の構文をより深く理解するために、新しい生徒の情報をすべて入力してください。

クエリの作成

簡単なクエリを作成するには

  • アプリケーションの Main メソッドで、実行されると最初のテストの点数が 90 点より高いすべての生徒のリストを生成する単純なクエリを作成します。 Student オブジェクト全体が選択されるので、クエリの型は IEnumerable<Student> になることに注意してください。 var キーワードを使用して暗黙の型指定を行うこともできますが、結果を明確に示すために、明示的な型指定を行います。 var の詳細については、「暗黙的に型指定されるローカル変数 (C# プログラミング ガイド)」を参照してください。

    クエリの範囲変数である student は、ソース内の各 Student への参照として機能します。これにより、各オブジェクトのメンバーにアクセスできるようになります。

// Create the query. 
// The first line could also be written as "var studentQuery ="
IEnumerable<Student> studentQuery =
    from student in students
    where student.Scores[0] > 90
    select student;

クエリの実行

クエリを実行するには

  1. クエリを実行する foreach ループを記述します。 このコードについて、次の点に注意してください。

    • 返されたシーケンス内の各要素へは、foreach ループ内の反復変数を通じてアクセスします。

    • この変数の型は Student で、クエリ変数の型は互換性のある IEnumerable<Student> です。

  2. このコードを追加したら、Ctrl キーを押しながら F5 キーを押してアプリケーションをビルドおよび実行し、[コンソール] ウィンドウで結果を確認します。

// Execute the query. 
// var could be used here also. 
foreach (Student student in studentQuery)
{
    Console.WriteLine("{0}, {1}", student.Last, student.First);
}

// Output: 
// Omelchenko, Svetlana 
// Garcia, Cesar 
// Fakhouri, Fadi 
// Feng, Hanying 
// Garcia, Hugo 
// Adams, Terry 
// Zabokritski, Eugene 
// Tucker, Michael

別のフィルター条件を追加するには

  • where 句の中で複数のブール型の条件を組み合わせると、さらに詳細なクエリを作成できます。 次のコードでは、条件を追加して、最初の点数が 90 点を上回り、最後の点数が 80 点未満の生徒がクエリから返されるようにします。 where 句は次のようになります。

    where student.Scores[0] > 90 && student.Scores[3] < 80
    

    詳細については、「where 句 (C# リファレンス)」を参照してください。

クエリの変更

結果を順序に従って並べるには

  1. 結果を一定の順序で並べると、結果のスキャンが簡単になります。 ソース要素のアクセス可能な任意のフィールドに基づいて、返されるシーケンスを順序付けることができます。 たとえば、次の orderby 句は、結果を各生徒の姓のアルファベット順 (A から Z) に並べます。 次の orderby 句を、クエリの where ステートメントの直後、select ステートメントの前に追加します。

    orderby student.Last ascending
    
  2. この orderby 句を変更して、最初のテストの点数に基づいて逆の順序 (最高点から最低点) に並べるようにします。

    orderby student.Scores[0] descending
    
  3. WriteLine の書式指定文字列を次のように変更して、点数を確認できるようにします。

    Console.WriteLine("{0}, {1} {2}", student.Last, student.First, student.Scores[0]);
    

    詳細については、「orderby 句 (C# リファレンス)」を参照してください。

結果をグループ化するには

  1. グループ化は、クエリ式の強力な機能です。 クエリで group 句を使用すると、グループのシーケンスが生成されます。各グループには、1 つの Key と、そのグループのすべてのメンバーで構成される 1 つのシーケンスが含まれます。 次の新しいクエリは、生徒の姓の頭文字をキーとして生徒をグループ化します。

    // studentQuery2 is an IEnumerable<IGrouping<char, Student>> 
    var studentQuery2 =
        from student in students
        group student by student.Last[0];
    
  2. ここでは、クエリの型が変わっていることに注意してください。 このクエリでは、char 型のキーを持つグループのシーケンスと、Student オブジェクトのシーケンスが生成されます。 クエリの型が変わっているため、次のコードでは foreach 実行ループも変更されています。

    // studentGroup is a IGrouping<char, Student> 
    foreach (var studentGroup in studentQuery2)
    {
        Console.WriteLine(studentGroup.Key);
        foreach (Student student in studentGroup)
        {
            Console.WriteLine("   {0}, {1}",
                      student.Last, student.First);
        }
    }
    
    // Output: 
    // O 
    //   Omelchenko, Svetlana 
    //   O'Donnell, Claire 
    // M 
    //   Mortensen, Sven 
    // G 
    //   Garcia, Cesar 
    //   Garcia, Debra 
    //   Garcia, Hugo 
    // F 
    //   Fakhouri, Fadi 
    //   Feng, Hanying 
    // T 
    //   Tucker, Lance 
    //   Tucker, Michael 
    // A 
    //   Adams, Terry 
    // Z 
    //   Zabokritski, Eugene
    
  3. Ctrl キーを押しながら F5 キーを押してアプリケーションを実行し、[コンソール] ウィンドウで結果を確認します。

    詳細については、「group 句 (C# リファレンス)」を参照してください。

変数の型を暗黙的に指定するには

  • IGroupingsIEnumerables を明示的にコーディングする方法は、すぐに面倒になる可能性があります。 var を使用すると、同じクエリと foreach ループをごく簡単に記述できます。 var キーワードは、オブジェクトの型を変更するのではなく、コンパイラに型を推論するように指示します。 studentQuery の型と反復変数 groupを var に変更して、クエリを再実行してください。 内部の foreach ループでは、反復変数の型が Student のままであるため、クエリは変更前と同様に機能します。 s 反復変数を var に変更して、クエリを再実行してください。 この場合も、変更前とまったく同じ結果が得られます。

    var studentQuery3 =
        from student in students
        group student by student.Last[0];
    
    foreach (var groupOfStudents in studentQuery3)
    {
        Console.WriteLine(groupOfStudents.Key);
        foreach (var student in groupOfStudents)
        {
            Console.WriteLine("   {0}, {1}",
                student.Last, student.First);
        }
    }
    
    // Output: 
    // O 
    //   Omelchenko, Svetlana 
    //   O'Donnell, Claire 
    // M 
    //   Mortensen, Sven 
    // G 
    //   Garcia, Cesar 
    //   Garcia, Debra 
    //   Garcia, Hugo 
    // F 
    //   Fakhouri, Fadi 
    //   Feng, Hanying 
    // T 
    //   Tucker, Lance 
    //   Tucker, Michael 
    // A 
    //   Adams, Terry 
    // Z 
    //   Zabokritski, Eugene
    

    var の詳細については、「暗黙的に型指定されるローカル変数 (C# プログラミング ガイド)」を参照してください。

キー値に基づいてグループを並べるには

  • 前のクエリを実行すると、グループはアルファベット順に並んでいないことがわかります。 これを変更するには、group 句の後に orderby 句を挿入します。 ただし、orderby 句を使用するには、まず group 句で作成されたグループへの参照として機能する識別子を指定する必要があります。 この識別子は、次のように into キーワードを使用することで指定します。

    var studentQuery4 =
        from student in students
        group student by student.Last[0] into studentGroup
        orderby studentGroup.Key
        select studentGroup;
    
    foreach (var groupOfStudents in studentQuery4)
    {
        Console.WriteLine(groupOfStudents.Key);
        foreach (var student in groupOfStudents)
        {
            Console.WriteLine("   {0}, {1}",
                student.Last, student.First);
        }
    }
    
    // Output: 
    //A 
    //   Adams, Terry 
    //F 
    //   Fakhouri, Fadi 
    //   Feng, Hanying 
    //G 
    //   Garcia, Cesar 
    //   Garcia, Debra 
    //   Garcia, Hugo 
    //M 
    //   Mortensen, Sven 
    //O 
    //   Omelchenko, Svetlana 
    //   O'Donnell, Claire 
    //T 
    //   Tucker, Lance 
    //   Tucker, Michael 
    //Z 
    //   Zabokritski, Eugene
    

    このクエリを実行すると、グループはアルファベット順に並べられます。

let を使用して識別子を指定するには

  • let キーワードを使用すると、クエリ式で任意の式の結果に識別子を割り当てることができます。 この識別子は、次の例のように利便性のために使用できるほか、式の結果が格納されるので何度も計算を行う必要がなくなり、パフォーマンスの向上につながります。

    // studentQuery5 is an IEnumerable<string> 
    // This query returns those students whose 
    // first test score was higher than their 
    // average score. 
    var studentQuery5 =
        from student in students
        let totalScore = student.Scores[0] + student.Scores[1] +
            student.Scores[2] + student.Scores[3]
        where totalScore / 4 < student.Scores[0]
        select student.Last + " " + student.First;
    
    foreach (string s in studentQuery5)
    {
        Console.WriteLine(s);
    }
    
    // Output: 
    // Omelchenko Svetlana 
    // O'Donnell Claire 
    // Mortensen Sven 
    // Garcia Cesar 
    // Fakhouri Fadi 
    // Feng Hanying 
    // Garcia Hugo 
    // Adams Terry 
    // Zabokritski Eugene 
    // Tucker Michael
    

    詳細については、「let 句 (C# リファレンス)」を参照してください。

クエリ式でメソッドの構文を使用するには

  • LINQ でのクエリ構文とメソッド構文 (C#)」で説明したように、一部のクエリ操作は、メソッドの構文を使用する方法でしか表すことができません。 次のコードは、ソース シーケンス内の各 Student の総合得点を計算し、そのクエリの結果に対して Average() メソッドを呼び出して、クラスの平均点を計算します。 クエリ式がかっこで囲まれていることに注意してください。

    var studentQuery6 =
        from student in students
        let totalScore = student.Scores[0] + student.Scores[1] +
            student.Scores[2] + student.Scores[3]
        select totalScore;
    
    double averageScore = studentQuery6.Average();
    Console.WriteLine("Class average score = {0}", averageScore);
    
    // Output: 
    // Class average score = 334.166666666667
    

select 句で型を変換または投影するには

  1. クエリでは、ソース シーケンス内の要素とは異なる要素のシーケンスが生成されることがよくあります。 前のクエリと実行ループを削除またはコメント アウトして、次のコードに置き換えてください。 このクエリでは、Students ではなく文字列のシーケンスが返されます。これは foreach ループにも影響します。

    IEnumerable<string> studentQuery7 =
        from student in students
        where student.Last == "Garcia" 
        select student.First;
    
    Console.WriteLine("The Garcias in the class are:");
    foreach (string s in studentQuery7)
    {
        Console.WriteLine(s);
    }
    
    // Output: 
    // The Garcias in the class are: 
    // Cesar 
    // Debra 
    // Hugo
    
  2. このチュートリアルで既に説明したコードによって、クラスの平均点はおよそ 334 点であることが示されました。 合計点がクラス平均を上回る Students のシーケンスを、それぞれの Student ID と共に生成するには、select ステートメントで匿名型を使用できます。

    var studentQuery8 =
        from student in students
        let x = student.Scores[0] + student.Scores[1] +
            student.Scores[2] + student.Scores[3]
        where x > averageScore
        select new { id = student.ID, score = x };
    
    foreach (var item in studentQuery8)
    {
        Console.WriteLine("Student ID: {0}, Score: {1}", item.id, item.score);
    }
    
    // Output: 
    // Student ID: 113, Score: 338 
    // Student ID: 114, Score: 353 
    // Student ID: 116, Score: 369 
    // Student ID: 117, Score: 352 
    // Student ID: 118, Score: 343 
    // Student ID: 120, Score: 341 
    // Student ID: 122, Score: 368
    

次の手順

C# におけるクエリの基本的な使用方法を理解したら、関心のある特定の種類の LINQ プロバイダーのドキュメントやサンプルに進むことができます。

LINQ to SQL [LINQ to SQL]

LINQ to DataSet

LINQ to XML

LINQ to Objects

参照

処理手順

チュートリアル: Visual Basic でクエリを記述する

概念

LINQ クエリ式 (C# プログラミング ガイド)

補助的な LINQ リソース

その他の技術情報

統合言語クエリ (LINQ: Language-Integrated Query)

C# の LINQ の概要