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


Возможности 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);

foreach Следующий цикл выполняет запросmyQuery1.

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);
}