Поделиться через


Функции 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);
}