LINQ をサポートする C# の機能

クエリ式

クエリ式は、SQL や XQuery に似た宣言型構文を使用して、System.Collections.Generic.IEnumerable<T> コレクションを照会します。 クエリ構文は、コンパイル時に、LINQ プロバイダーの標準クエリ メソッドの実装に対するメソッド呼び出しに変換されます。 アプリケーションは、using ディレクティブを使用して適切な名前空間を指定することにより、スコープ内の標準クエリ演算子を制御します。 次のクエリ式では、文字列の配列を受け取り、文字列の最初の文字を基に文字列をグループ化し、グループを並び替えています。

var query = from str in stringArray
            group str by str[0] into stringGroup
            orderby stringGroup.Key
            select stringGroup;

暗黙的に型指定された変数 (var)

次に示すように、var 修飾子を使用して、型を推論して割り当てるようにコンパイラに指示できます。

var number = 5;
var name = "Virginia";
var query = from str in stringArray
            where str[0] == 'm'
            select str;

var として宣言された変数は、明示的に型を指定した変数とまったく同じように厳密に型指定されます。 var を使用すると匿名型を作成できますが、ローカル変数にのみ使用できます。 詳細については、「暗黙的に型指定されるローカル変数」を参照してください。

オブジェクト初期化子とコレクション初期化子

オブジェクト初期化子とコレクション初期化子を使用すると、オブジェクトのコンストラクターを明示的に呼び出さなくても、オブジェクトを初期化できます。 通常、初期化子は、ソース データを新しいデータ型に投影するクエリ式で使用されます。 パブリックな Name プロパティと Phone プロパティを持つ Customer という名前のクラスがある場合、オブジェクト初期化子は次のコードのように使用できます。

var cust = new Customer { Name = "Mike", Phone = "555-1212" };

引き続き Customer クラスの例を進めます。IncomingOrders というデータ ソースがあり、OrderSize が大きな注文ごとに、その注文に基づいて新しい Customer を作成するとします。 LINQ クエリをこのデータ ソースで実行し、オブジェクト初期化を使用してコレクションを満たすことができます。

var newLargeOrderCustomers = from o in IncomingOrders
                            where o.OrderSize > 5
                            select new Customer { Name = o.Name, Phone = o.Phone };

データ ソースには、OrderSize など、Customer クラス以外にもプロパティが定義されている可能性がありますが、オブジェクト初期化を利用することで、クエリから返されるデータは目的のデータ型に形成されます。今回のクラスに関連するデータを選択します。 その結果、必要とする新しい CustomerSystem.Collections.Generic.IEnumerable<T> に入力されました。 上記の例は次のような LINQ のメソッド構文でも記述できます。

var newLargeOrderCustomers = IncomingOrders.Where(x => x.OrderSize > 5).Select(y => new Customer { Name = y.Name, Phone = y.Phone });

C# 12 以降では、コレクション式を使用してコレクションを初期化できます。

詳細については、以下を参照してください:

匿名型

コンパイラは、匿名型を構築します。 型名はコンパイラでのみ使用できます。 匿名型を使用すると、個別に名前付き型を定義しなくても、クエリ結果内のプロパティのセットを一時的にグループ化できるため便利です。 次に示すように、匿名型は new 式とオブジェクト初期化子を使用して初期化されます。

select new {name = cust.Name, phone = cust.Phone};

C# 7 以降では、タプルを使用して、名前のない型を作成できます。

拡張メソッド

拡張メソッドは型に関連付けることができる静的メソッドであるため、その型のインスタンス メソッドと同じように呼び出すことができます。 この機能を使用すると、既存の型を実際に変更しなくても、その型に新しいメソッドを実質的に "追加" できます。 標準クエリ演算子は、IEnumerable<T> を実装する任意の型で LINQ クエリ機能を実現する拡張メソッドのセットです。

ラムダ式

ラムダ式=> 演算子を使用して関数本体からパラメーター入力を分離するインライン関数であり、コンパイル時にデリゲートまたは式ツリーに変換できます。 LINQ プログラミングでは、標準クエリ演算子に対する直接メソッド呼び出しを行う場合にラムダ式が使用されます。

データとしての式

クエリ オブジェクトはコンポーザブルです。つまり、メソッドからクエリを返すことができます。 クエリを表すオブジェクトには、結果のコレクションではなく、必要に応じて結果を生成する手順が保存されます。 メソッドからクエリ オブジェクトを返すメリットは、そのオブジェクトをさらに構成したり変更したりできることです。 そのため、クエリを返すメソッドの戻り値または out パラメーターも同じ型である必要があります。 メソッドは、クエリを具象型 List<T> または Array 型に具体化した場合、クエリ自体ではなくクエリ結果を返します。 メソッドから返されたクエリ変数は、引き続き構成または変更できます。

次の例で、1 番目のメソッド QueryMethod1 はクエリを戻り値として返し、2 番目のメソッド QueryMethod2 はクエリを out パラメーター (例では returnQ) として返します。 どちらの場合も、クエリ結果ではなく、クエリが返されます。

IEnumerable<string> QueryMethod1(int[] ints) =>
    from i in ints
    where i > 4
    select i.ToString();

void QueryMethod2(int[] ints, out IEnumerable<string> returnQ) =>
    returnQ =
        from i in ints
        where i < 4
        select i.ToString();

int[] nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

var myQuery1 = QueryMethod1(nums);

クエリ myQuery1 は、次の foreach ループで実行されます。

foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}

マウス ポインターで myQuery1 をポイントすると、その型が表示されます。

myQuery1 を使わず、直接 QueryMethod1 から返されたクエリも実行できます。

foreach (var s in QueryMethod1(nums))
{
    Console.WriteLine(s);
}

QueryMethod1 の呼び出しをマウス ポインターでポイントすると、戻り値の型が表示されます。

QueryMethod2 は、クエリを out パラメーターの値として返します。

QueryMethod2(nums, out IEnumerable<string> myQuery2);

// Execute the returned query.
foreach (var s in myQuery2)
{
    Console.WriteLine(s);
}

クエリの構成を使って、クエリを変更できます。 この例では、前のクエリ オブジェクトを使って新しいクエリ オブジェクトが作成されています。 この新しいオブジェクトは、元のクエリ オブジェクトとは異なる結果を返します。

myQuery1 =
    from item in myQuery1
    orderby item descending
    select item;

// Execute the modified query.
Console.WriteLine("\nResults of executing modified myQuery1:");
foreach (var s in myQuery1)
{
    Console.WriteLine(s);
}