Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Внутреннее представление операторов запросов LINQ

К настоящему моменту вы уже знакомы с процессом построения выражений запросов с применением разнообразных операций запросов C# (таких как

from
,
in
,
where
,
orderby
и
select
). Вдобавок вы узнали, что определенная функциональность API-интерфейса LINQ to Objects доступна только через вызов расширяющих методов класса
Enumerable
. В действительности при компиляции запросов LINQ компилятор C# транслирует все операции LINQ в вызовы методов класса
Enumerable
.

Огромное количество методов класса

Enumerable
прототипированы для приема делегатов в качестве аргументов. Многие методы требуют обобщенный делегат по имени
Funс<>
, который был описан во время рассмотрения обобщенных делегатов в главе 10. Взгляните на метод
Where
класса
Enumerable
, вызываемый автоматически в случае использования операции
where
:

// Перегруженные версии метода Enumerable.Where<T>.

// Обратите внимание, что второй параметр имеет тип System.Func<>.

public static IEnumerable<TSource> Where<TSource>(

this IEnumerable<TSource> source,

System.Func<TSource,int,bool> predicate)

public static IEnumerable<TSource> Where<TSource>(

this IEnumerable<TSource> source,

System.Func<TSource,bool> predicate)

Делегат

Func<>
представляет шаблон фиксированной функции с набором до 16 аргументов и возвращаемым значением. Если вы исследуете этот тип в браузере объектов Visual Studio, то заметите разнообразные формы делегата
Func<>
. Например:

// Различные формы делегата Func<>.

public delegate TResult Func<T1,T2,T3,T4,TResult>

(T1 arg1, T2 arg2, T3 arg3, T4 arg4)

public delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3)

public delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2)

public delegate TResult Func<T1,TResult>(T1 arg1)

public delegate TResult Func<TResult>

Учитывая, что многие члены класса

System.Linq.Enumerable
при вызове ожидают получить делегат, можно вручную создать новый тип делегата и написать для него необходимые целевые методы, применить анонимный метод C# или определить подходящее лямбда-выражение. Независимо от выбранного подхода конечный результат будет одним и тем же.

Хотя использование операций запросов LINQ является, несомненно, самым простым способом построения запросов LINQ, давайте взглянем на все возможные подходы, чтобы увидеть связь между операциями запросов C# и лежащим в основе типом

Enumerable
.

Построение выражений запросов с применением операций запросов

Для начала создадим новый проект консольного приложения по имени

LinqUsingEnumerable
. В классе
Program
будут определены статические вспомогательные методы (вызываемые внутри операторов верхнего уровня) для иллюстрации разнообразных подходов к построению выражений запросов LINQ.

Первый метод,

QueryStringsWithOperators
, предлагает наиболее прямолинейный способ создания выражений запросов и идентичен коду примера
LinqOverArray
, который приводился ранее в главе:

using System.Linq;

static void QueryStringWithOperators

{

Console.WriteLine("***** Using Query Operators *****");

string[] currentVideoGames = {"Morrowind", "Uncharted 2",

"Fallout 3", "Daxter", "System
Shock 2"};

var subset = from game in currentVideoGames

where game.Contains(" ") orderby game select game;

foreach (string s in subset)

{

Console.WriteLine("Item: {0}", s);

}

}

Очевидное

преимущество использования операций запросов C# при построении выражений запросов заключается в том, что делегаты
Funс<>
и вызовы методов
Enumerable
остаются вне поля зрения и внимания, т.к. выполнение необходимой трансляции возлагается на компилятор С#. Бесспорно, создание выражений LINQ с применением различных операций запросов (
from
,
in
,
where
или
orderby
) является наиболее распространенным и простым подходом.

Построение выражений запросов с использованием типа Enumerable и лямбда-выражений

Имейте в виду, что применяемые здесь операции запросов LINQ представляют собой сокращенные версии вызова расширяющих методов, определенных в типе

Enumerable
. Рассмотрим показанный ниже метод
QueryStringsWithEnumerableAndLambdas
, который обрабатывает локальный массив строк, но на этот раз в нем напрямую используются расширяющие методы
Enumerable
:

static void QueryStringsWithEnumerableAndLambdas

{

Console.WriteLine("***** Using Enumerable / Lambda Expressions *****");

string[] currentVideoGames = {"Morrowind", "Uncharted 2",

"Fallout 3", "Daxter", "System
Shock 2"};

// Построить выражение запроса с использованием расширяющих методов,

// предоставленных типу Array через тип Enumerable.

var subset = currentVideoGames

.Where(game => game.Contains(" "))

.OrderBy(game => game).Select(game => game);

// Вывести результаты.

foreach (var game in subset)

{

Console.WriteLine("Item: {0}", game);

}

Console.WriteLine;

}

Здесь сначала вызывается расширяющий метод

Where
на строковом массиве
currentVideoGames
. Вспомните, что класс
Array
получает данный метод от класса
Enumerable
. Метод
Enumerable.Where
требует параметра типа делегата
System.Func<T1,TResult>
. Первый параметр типа упомянутого делегата представляет совместимые с интерфейсом
IEnumerable<T>
данные для обработки (массив строк в рассматриваемом случае), а второй — результирующие данные метода, которые получаются от единственного оператора, вставленного в лямбда-выражение.

Поделиться с друзьями: