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