from 句 (C# リファレンス)

クエリ式は、from 句で始める必要があります。 また、クエリ式にはサブクエリを含めることができます。サブクエリも from 句で始めます。 from 句は次を指定します。

  • クエリまたはサブクエリを実行するデータ ソース。

  • ソース シーケンスの各要素を表す、ローカルの範囲変数

範囲変数とデータ ソースの両方は厳密に型指定されます。 from 句で参照されるデータ ソースには、IEnumerable 型、IEnumerable<T> 型、あるいは IQueryable<T> のような派生型が含まれている必要があります。

次の例では、numbers はデータ ソースであり、num は範囲変数です。 var キーワードが使用されていても、両方の変数が厳密に型指定されていることに注目してください。

class LowNums
{
    static void Main()
    {
        // A simple data source.
        int[] numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];

        // Create the query.
        // lowNums is an IEnumerable<int>
        var lowNums = from num in numbers
            where num < 5
            select num;

        // Execute the query.
        foreach (int i in lowNums)
        {
            Console.Write(i + " ");
        }
    }
}
// Output: 4 1 3 2 0

範囲変数

データ ソースが IEnumerable<T> を実装するとき、コンパイラは範囲変数の型を推測します。 たとえば、ソースの型が IEnumerable<Customer> の場合、範囲変数は Customer ではないかと推測されます。 ソースが ArrayList のような非ジェネリック IEnumerable 型のときにのみ、型を明示的に指定する必要があります。 詳細については、「LINQ を使用して ArrayList にクエリを実行する方法」を参照してください。

前述の例では、num は型 int として推測されます。 範囲変数は厳密に型指定されるため、範囲変数の上でメソッドを呼び出したり、他の操作で範囲変数を使用したりできます。 たとえば、select num を記述する代わりに、select num.ToString() を記述し、クエリ式が整数ではなく文字列のシーケンスを返すようにできます。 あるいは、式でシーケンス 14、11、13、12、10 を返すように select num + 10 を記述できます。 詳細については、「select 句」をご覧ください。

範囲変数は foreach ステートメントの繰り返し変数に似ていますが、1 つだけ非常に重要な違いがあります。範囲変数がソースのデータを格納することは決してありません。 これは構文上の利便性のためです。クエリの実行時に何が起こるのかクエリで表現できます。 詳細については、「LINQ クエリの概要 (C#)」を参照してください。

複合 from 句

ソース シーケンスの各要素がそれ自体シーケンスになったり、それ自体にシーケンスが含まれたりすることがあります。 たとえば、データ ソースが IEnumerable<Student> になることがあります。この場合、シーケンスの各学生オブジェクト参照にテストの点数の一覧が含まれます。 各 Student 要素内の内部一覧にアクセスするには、複合 from 句を利用できます。 この手法は、foreach ステートメントを入れ子にして使う場合に似ています。 where 句または orderby 句をいずれかの from 句に追加し、結果を絞り込むことができます。 次は、Student オブジェクトのシーケンスの例です。テストの点数を表す整数の内部 List がそれぞれに含まれています。 内部一覧にアクセスするには、複合 from 句を利用できます。 必要に応じて、2 つの from 句の間に句を挿入できます。

class CompoundFrom
{
    // The element type of the data source.
    public class Student
    {
        public required string LastName { get; init; }
        public required List<int> Scores {get; init;}
    }

    static void Main()
    {

        // Use a collection initializer to create the data source. Note that
        // each element in the list contains an inner sequence of scores.
        List<Student> students =
        [
           new Student {LastName="Omelchenko", Scores= [97, 72, 81, 60]},
           new Student {LastName="O'Donnell", Scores= [75, 84, 91, 39]},
           new Student {LastName="Mortensen", Scores= [88, 94, 65, 85]},
           new Student {LastName="Garcia", Scores= [97, 89, 85, 82]},
           new Student {LastName="Beebe", Scores= [35, 72, 91, 70]}
        ];

        // Use a compound from to access the inner sequence within each element.
        // Note the similarity to a nested foreach statement.
        var scoreQuery = from student in students
                         from score in student.Scores
                            where score > 90
                            select new { Last = student.LastName, score };

        // Execute the queries.
        Console.WriteLine("scoreQuery:");
        // Rest the mouse pointer on scoreQuery in the following line to
        // see its type. The type is IEnumerable<'a>, where 'a is an
        // anonymous type defined as new {string Last, int score}. That is,
        // each instance of this anonymous type has two members, a string
        // (Last) and an int (score).
        foreach (var student in scoreQuery)
        {
            Console.WriteLine("{0} Score: {1}", student.Last, student.score);
        }
    }
}
/*
scoreQuery:
Omelchenko Score: 97
O'Donnell Score: 91
Mortensen Score: 94
Garcia Score: 97
Beebe Score: 91
*/

複数の from 句を使用して結合を実行する

複合 from 句を使用し、単一データ ソースの内部コレクションにアクセスします。 ただし、個別データ ソースから補足クエリを生成する複数の from 句をクエリに含めることもできます。 この手法では、join 句で不可能な特定の結合操作を実行できます。

2 つの from 句を使用し、2 つのデータ ソースの完全なクロス結合を作る様子を示したのが次の例です。

class CompoundFrom2
{
    static void Main()
    {
        char[] upperCase = ['A', 'B', 'C'];
        char[] lowerCase = ['x', 'y', 'z'];

        // The type of joinQuery1 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery1 =
            from upper in upperCase
            from lower in lowerCase
            select new { upper, lower };

        // The type of joinQuery2 is IEnumerable<'a>, where 'a
        // indicates an anonymous type. This anonymous type has two
        // members, upper and lower, both of type char.
        var joinQuery2 =
            from lower in lowerCase
            where lower != 'x'
            from upper in upperCase
            select new { lower, upper };

        // Execute the queries.
        Console.WriteLine("Cross join:");
        // Rest the mouse pointer on joinQuery1 to verify its type.
        foreach (var pair in joinQuery1)
        {
            Console.WriteLine("{0} is matched to {1}", pair.upper, pair.lower);
        }

        Console.WriteLine("Filtered non-equijoin:");
        // Rest the mouse pointer over joinQuery2 to verify its type.
        foreach (var pair in joinQuery2)
        {
            Console.WriteLine("{0} is matched to {1}", pair.lower, pair.upper);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
        Cross join:
        A is matched to x
        A is matched to y
        A is matched to z
        B is matched to x
        B is matched to y
        B is matched to z
        C is matched to x
        C is matched to y
        C is matched to z
        Filtered non-equijoin:
        y is matched to A
        y is matched to B
        y is matched to C
        z is matched to A
        z is matched to B
        z is matched to C
        */

複数の from 句を使用する結合操作の詳細については、「左外部結合の実行」を参照してください。

関連項目