Предложение 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. Явно задавать тип необходимо только в том случае, если источник имеет не являющийся универсальным тип IEnumerable, например ArrayList. Дополнительные сведения см. в статье Практическое руководство. Выполнение запроса к ArrayList с помощью LINQ (C#).

В предыдущем примере num выводится к типу int. Поскольку переменная диапазона строго типизирована, вы можете использовать ее в вызовах методов и других операциях. Например, вместо кода select num можно использовать запись select num.ToString(), в результате чего выражение запроса будет возвращать последовательность строк вместо целых чисел. Также можно использовать запись select num + 10, в результате чего выражение будет возвращать последовательность 14, 11, 13, 12, 10. Дополнительные сведения см. в разделе Предложение select.

Переменные диапазона схожи с переменными итерации в инструкции foreach, за исключением одного важного отличия: в переменной диапазона никогда не хранятся фактические данные из источника. Это лишь способ оптимизировать код, позволяющий запросу описывать действия, которые произойдут при выполнении запроса. Дополнительные сведения см. в разделе Введение в запросы LINQ (C#).

Составные предложения from

В некоторых случаях каждый элемент в последовательности источника может сам представлять собой последовательность или содержать ее. Например, в качестве источника данных может выступать IEnumerable<Student>, в котором каждый объект учащегося представляет собой последовательность, содержащую список полученных оценок. Для доступа к внутреннему списку каждого элемента Student можно использовать составные предложения from. Этот способ аналогичен использованию вложенных предложений foreach. Для фильтрации результатов можно добавить предложения where или orderby в любое из предложений from. В следующем примере показана последовательность объектов Student, каждый из которых содержит внутренний список List с целыми числами, соответствующими оценкам. Для доступа к внутреннему списку следует использовать предложение from. При необходимости вы можете вставлять предложения между двумя предложениями 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.

В следующем примере показано, как можно выполнить полное перекрестное соединение двух источников данных с помощью двух предложений from.

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, см. в разделе Выполнение левых внешних соединений.

См. также