Возможности C#, поддерживающие LINQ
Выражения запросов
Выражения запросов используют декларативный синтаксис, аналогичный 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
позволяет создавать анонимные типы, но только для локальных переменных. Дополнительные сведения см. в разделе Неявно типизированные локальные переменные.
Инициализаторы объектов и коллекций
Инициализаторы объектов и коллекций позволяют инициализировать объекты без явного вызова конструктора для объекта. Инициализаторы обычно используются в выражениях запросов при проецировании исходных данных в новый тип данных. При наличии, например, класса с именем Customer
с общедоступными свойствами Name
и Phone
инициализатор объектов можно использовать как в следующем примере кода:
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 };
Источник данных может иметь больше свойств, определенных, чем Customer
класс, например OrderSize
, но при инициализации объектов данные, возвращаемые из запроса, формируются в нужный тип данных; вы выбираете данные, относящиеся к классу. В результате у вас теперь есть заполнены System.Collections.Generic.IEnumerable<T> новые Customer
, которые вы хотели. Предыдущий пример также можно записать в синтаксисе метода LINQ:
var newLargeOrderCustomers = IncomingOrders.Where(x => x.OrderSize > 5).Select(y => new Customer { Name = y.Name, Phone = y.Phone });
Начиная с C# 12, можно использовать выражение коллекции для инициализации коллекции.
Дополнительные сведения см. в разделе:
Анонимные типы
Компилятор создает анонимный тип. Имя типа доступно только компилятору. Анонимные типы обеспечивают удобный способ временной группировки набора свойств в результатах запроса без необходимости определения отдельного именованного типа. Анонимные типы инициализируются с помощью нового выражения и инициализатора объектов, как показано ниже:
select new {name = cust.Name, phone = cust.Phone};
Начиная с C# 7, можно использовать кортежи для создания неименованных типов.
Методы расширения
Метод расширения — это статический метод, который может быть связан с типом, чтобы его можно было вызывать, как если бы он был методом экземпляра в типе. Эта возможность позволяет, по сути, "добавлять" новые методы в существующие типы, фактически не изменяя их. Стандартные операторы запросов — это набор методов расширения, предоставляющий функции запросов LINQ для любого типа, реализующего IEnumerable<T>.
Лямбда-выражения
Лямбда-выражения — это встроенная функция, которая использует =>
оператор для разделения входных параметров из текста функции и может быть преобразована во время компиляции в делегат или дерево выражений. В программировании LINQ вы столкнетесь с лямбда-выражениями при выполнении прямых вызовов к стандартным операторам запросов.
Выражения в виде данных
Объекты запроса являются составляемыми, что означает возможность возврата запроса из метода. Объекты, представляющие запросы, не хранят полученную коллекцию, а действия по получению результатов при необходимости. Преимущество возвращения объектов запроса заключается в том, что их можно комбинировать или изменять. Поэтому любое возвращаемое значение или параметр out
метода, который возвращает запрос, должны также иметь этот тип. Если метод материализует запрос в конкретный List<T> или Array тип, он возвращает результаты запроса вместо самого запроса. Переменную запроса, возвращаемую из метода, можно формировать или изменять.
В следующем примере первый метод QueryMethod1
возвращает запрос в качестве возвращаемого значения, а второй метод 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
мыши на указатель мыши, чтобы просмотреть его тип.
Вы также можете выполнить запрос, возвращаемый напрямую QueryMethod1
, без использования myQuery1
.
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);
}