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

LINQとは、さまざまなデータソースを共通の方法で処理できる仕組みです。

データソース (data source)

データソース 技術 参照
オブジェクト (.NETコレクション、ファイル、文字列など) LINQ to Objects
データセット (DataSet) LINQ to DataSet  
SQL Server LINQ to SQL (DLINQ)
エンティティ (Entity Framework : EF) を介した、リレーショナル データベース。データ プロバイダが提供されているならば、SQL Server以外にも対応 LINQ to Entities
メモリ内のXML文書 (XML documents) LINQ to XML (XLINQ)  

データソースの型

  • IEnumerable<T> インターフェイス
  • IQueryable<T> インターフェイス

クエリ式 (query expression)

int[] values = { 1, 2, 3, 4, 5 };

IEnumerable<int> query =
    from a in values
    where a > 3
    select a;

foreach (int variable in query)
{
    Console.Write(variable); // 4, 5
}
C# の統合言語クエリ (LINQ) | Microsoft Learn
句 (clause) 用途
from クエリを実行するデータソース。またはローカルの範囲変数
where 結果のフィルタ
select 結果の型と形状
group 結果のグループ化
orderby 結果の並べ替え
join 2つのデータソースを結合
into select、group、joinの結果を参照するための識別子
let クエリ式の結果を格納
クエリ キーワード - C# リファレンス | Microsoft Learn

クエリ式はfrom句で始め、groupまたはselect句で終えます。

from句

クエリまたはサブクエリを実行するデータソース、またはソース シーケンスの各要素を表すローカルの範囲変数を指定します。from 句 - C# リファレンス | Microsoft Learn

from 範囲変数 in データソース

データソースはIEnumerable、IEnumerable<T>またはIQueryable<T>の派生型である必要があります。

データソースがList<int>ならば範囲変数 (range variable) はintと推定されますが、ArrayListのような非ジェネリックの型の場合、型を指定してキャストさせる必要があります。LINQ を使用して ArrayList にクエリを実行する方法 (C#) | Microsoft Learn

ArrayList arrayList = new ArrayList { 1, 2, 3 };

IEnumerable<int> query1 =
    from num in arrayList // error CS1934: ソース型 'ArrayList' のクエリ パターンの実装が見つかりませんでした。'Select' が見つかりません。範囲変数 'a'  の型を明示的に指定してください。
    select num;

IEnumerable<int> query2 =
    from int num in arrayList // ok
    select num;

データソースがIEnumerableのときは、すべての要素がIEnumerable<T>へキャストされた上で処理されます。

where句

どの要素がクエリ式から返されるかを指定します。where 句 - C# リファレンス | Microsoft Learn

記述する内容
単一の述語 (predicate) を記述 where num < 5
&&や||演算子を使用して、複数の述語を記述 where num < 5 && num % 2 == 0
演算子を省略して、複数の述語を記述 (&&演算子を指定したと見なされる) where num < 5 where num % 2 == 0
メソッドを記述 where IsEven(num)

whereキーワードは、コンパイル時にWhere()メソッドの呼び出しに変換されます。また記述した式は、クエリの結果を要求されたときに評価されます。

select句

クエリが実行されたときに生成される値の、型を指定します。

select 句 - C# リファレンス - C# | Microsoft Learn
int[] values = { 0x61, 0x62, 0x63 };

IEnumerable<char> query =
    from num in values
    select (char)num;

query.ToArray(); // 'a', 'b', 'c'

group句

string[] words = { "B1", "A1", "A2", "B2" };

IEnumerable<IGrouping<char, string>> query =
    from word in words
    group word by word[0];

Dictionary<char, int> dic = query.ToDictionary(s => s.Key, s => s.Count());
// {[B, 2]}
// {[A, 2]}

グループに追加の操作をするならば、intoで一時的な識別子を作成しクエリを続け、selectまたは別のgroup句で終了します。

IEnumerable<IGrouping<char, string>> query =
    from word in words
    group word by word[0] into c
    orderby c.Key
    select c;

複数のキーで要素をグループ化するには、複合キー (composite keys) を用います。複合キーでグループ化する - group 句 - C# リファレンス - C# | Microsoft Learn

string[] words = { "B1", "A1", "A2", "B2", "B30" };

var query =
    from word in words
    group word by new { c = word[0], length = word.Length };

var dic = query.ToDictionary(s => s.Key, s => s.Count());
// {[{ c = B, length = 2 }, 2]}
// {[{ c = A, length = 2 }, 2]}
// {[{ c = B, length = 3 }, 1]}

このときの複合キーを匿名型 (anonymous type) ではなく名前付きの型 (named type) とするには、キーとなる構造体を

struct MyStruct
{
    public char c;
    public int length;
}

のように定義して

IEnumerable<IGrouping<MyStruct, string>> query =
    from word in words
    group word by new MyStruct { c = word[0], length = word.Length };

のように利用します。このとき構造体ではなくクラスとするには、Equals()とGetHashCode()をオーバーライドして定義します。

class MyClass
{
    public char c;
    public int length;

    public override bool Equals(object obj)
    {
        MyClass other = (MyClass)obj;
        return other.c == this.c && other.length == this.length;
    }

    public override int GetHashCode()
    {
        string str = $"{this.c}{this.length}";
        return str.GetHashCode();
    }
}
ディレクトリ ツリーで重複するファイルを照会する方法 (LINQ) (C#) - C# | Microsoft Learn

orderby句

返されるシーケンスまたはサブシーケンス (グループ) を、昇順または降順で並べ替えられます。orderby 句 - C# リファレンス | Microsoft Learn

クエリ構文 メソッド構文 機能
orderby OrderBy() 要素を、昇順に並べ替える
orderby … descending OrderByDescending() 要素を、降順に並べ替える
orderby …, … ThenBy() 後続の要素を、昇順に並べ替える
orderby …, … descending ThenByDescending() 後続の要素を、降順に並べ替える
なし Reverse() 要素の順序を反転させる
メソッド - データの並べ替え (C#) | Microsoft Learn
string[] words = { "B", "A", "D1", "C1" };

IEnumerable<string> query1 =
    from word in words
    orderby word.Length
    select word;
    // "B", "A", "D1", "C1"

IEnumerable<string> query2 =
    from word in words
    orderby word[0] descending
    select word;
    // "D1", "C1", "B", "A"

IEnumerable<string> query3 =
    from word in words
    orderby word.Length, word[0]
    select word;
    // "A", "B", "C1", "D1"

join句

直接の関連のない異なるソース シーケンスの要素を関連付けられます。join 句 - C# リファレンス | Microsoft Learn

結合方法 機能
内部結合 (Inner join)  
グループ結合 (Group join) 結果を配列に格納
外部結合 (Left outer join) 一致する要素がないときに、既定の要素を格納

join句の後にintoがなければJoin()に、あればGroupJoin()に変換されます。

用語

クエリ変数 (query variable)

LINQではクエリの結果ではなく、クエリを格納する変数をクエリ変数と呼びます。クエリ変数 - クエリ式の基本 (C# での LINQ) | Microsoft Learn

クエリ構文 (query syntax) とメソッド構文 (method syntax)

クエリを用いたクエリ構文は、コンパイル時にメソッド構文に変換されます。

クエリの例 対応するメソッド
from from int i in numbers Cast()
from 複数のfrom句 SelectMany
where where Where()
select select Select()
group
  • クエリを続行しないならば、group … by
  • クエリを続行するならば、group … by … into …
GroupBy()
orderby orderby OrderBy()
orderby orderby … descending OrderByDescending()
orderby orderby …, … ThenBy()
orderby orderby …, … descending ThenByDescending()
join join … in … on … equals … into … GroupJoin()
join join … in … on … equals … Join()
Query Expression Syntax Table - Query Expression Syntax for Standard Query Operators (C#) | Microsoft Learn
int[] values = { 1, 2, 3, 4, 5 };

// クエリ構文
IEnumerable<int> query1 =
    from num in values
    where num > 3
    select num;

// メソッド構文
IEnumerable<int> query2 =
    values.Cast<int>()
    .Where(a => a > 3)
    .Select(a => a);

Count()やMax()などにはクエリ式の句がないため、メソッドを用いる必要があります。

標準クエリ演算子 (standard query operators)

  • IEnumerable<T> 型のオブジェクトを操作する演算子
  • IQueryable<T> 型のオブジェクトを操作する演算子

処理の進捗

データを評価するごとに呼び出される式で処理数を計数することで、処理の進捗を示せます。

int count = 0;

IEnumerable<int> source = Enumerable.Range(0, 10);
IEnumerable<int> query = source.Where((x) =>
{
    Console.WriteLine(count++); // 処理数を数える

    return x % 2 == 0;
});

query.ToArray();
Microsoft Learnから検索