Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
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> данные для обработки (массив строк в рассматриваемом случае), а второй — результирующие данные метода, которые получаются от единственного оператора, вставленного в лямбда-выражение.
Возвращаемое значение метода Where() в приведенном примере кода скрыто от глаз, но "за кулисами" работа происходит с типом OrderedEnumerable. На объекте указанного типа вызывается обобщенный метод OrderBy(), который также принимает параметр типа делегата Func<>. Теперь производится передача всех элементов по очереди посредством подходящего лямбда-выражения. Результатом вызова OrderBy() является новая упорядоченная последовательность первоначальных данных.
И, наконец, осуществляется вызов метода Select() на последовательности, возвращенной OrderBy(), который в итоге дает финальный набор данных, сохраняемый в неявно типизированной переменной по имени subset.
Конечно, такой "длинный" запрос LINQ несколько сложнее для восприятия, чем предыдущий пример с операциями запросов LINQ. Без сомнения, часть сложности связана с объединением в цепочку вызовов посредством операции точки. Вот тот же самый запрос с выделением каждого шага в отдельный фрагмент (разбивать запрос на части можно разными способами):
static void QueryStringsWithEnumerableAndLambdas2()
{
Console.WriteLine("***** Using Enumerable / Lambda Expressions *****");
string[] currentVideoGames = {"Morrowind", "Uncharted 2",
"Fallout 3", "Daxter", "System Shock 2"};
// Разбить на части.
var gamesWithSpaces =
currentVideoGames.Where(game => game.Contains(" "));
var orderedGames = gamesWithSpaces.OrderBy(game => game);
var subset = orderedGames.Select(game => game);
foreach (var game in subset)
{
Console.WriteLine("Item: {0}", game);
}
Console.WriteLine();
}
Как видите, построение выражения запроса LINQ с применением методов класса Enumerable напрямую приводит к намного более многословному запросу, чем в случае использования операций запросов С#. Кроме того, поскольку методы Enumerable требуют передачи делегатов в качестве параметров, обычно необходимо писать лямбда-выражения, чтобы обеспечить обработку входных данных внутренней целью делегата.
Построение выражений запросов с использованием типа Enumerable и анонимных методов
Учитывая, что лямбда-выражения C# — это просто сокращенный способ работы с анонимными методами, рассмотрим третье выражение запроса внутри вспомогательного метода QueryStringsWithAnonymousMethods():
static void QueryStringsWithAnonymousMethods()
{
Console.WriteLine("***** Using Anonymous Methods *****");
string[] currentVideoGames = {"Morrowind", "Uncharted 2",
"Fallout 3", "Daxter", "System Shock 2"};
// Построить необходимые делегаты Func<>
// с использованием анонимных методов.
Func<string, bool> searchFilter =
delegate(string game) { return game.Contains(" "); };
Func<string, string> itemToProcess = delegate(string s) { return s; };
// Передать делегаты в методы класса Enumerable.
var subset =
currentVideoGames.Where(searchFilter).OrderBy(itemToProcess).
Select(itemToProcess);
// Вывести результаты.
foreach (var game in subset)
{
Console.WriteLine("Item: {0}", game);
}
Console.WriteLine();
}
Такой вариант выражения запроса оказывается еще более многословным из-за создания вручную делегатов Func<>, применяемых методами Where(), OrderBy() и Select() класса Enumerable. Положительная сторона данного подхода связана с тем, что синтаксис анонимных методов позволяет заключить всю обработку, выполняемую делегатами, в единственное определение метода. Тем не менее, этот метод функционально эквивалентен методам QueryStringsWithEnumerableAndLambdas() и QueryStringsWithOperators(), созданным в предшествующих разделах.
Построение выражений запросов с использованием типа Enumerable и низкоуровневых делегатов
Наконец, если вы хотите строить выражение запроса с применением многословного подхода, то можете отказаться от использования синтаксиса лямбда-выражений и анонимных методов и напрямую создавать цели делегатов для каждого типа Func<>. Ниже показана финальная версия выражения запроса, смоделированная внутри нового типа класса по имени VeryComplexQueryExpression:
class VeryComplexQueryExpression
{
public static void QueryStringsWithRawDelegates()
{
Console.WriteLine("***** Using Raw Delegates *****");
string[] currentVideoGames = {"Morrowind", "Uncharted 2",
"Fallout 3", "Daxter", "System Shock 2"};
// Построить необходимые делегаты Func<>.
Func<string, bool> searchFilter =
new Func<string, bool>(Filter);
Func<string, string> itemToProcess =
new Func<string,string>(ProcessItem);
// Передать делегаты в методы класса Enumerable.
var subset =
currentVideoGames
.Where(searchFilter)
.OrderBy(itemToProcess)
.Select(itemToProcess);
// Вывести результаты.
foreach (var game in subset)
{
Console.WriteLine("Item: {0}", game);
}
Console.WriteLine();
}
// Цели делегатов.
public static bool Filter(string game)
{
return game.Contains(" ");
}
public static string ProcessItem(string game)
{
return game;
}
}
Чтобы протестировать такую версию логики обработки строк, метод QueryStringsWithRawDelegates() понадобится вызвать внутри операторов верхнего уровня в классе Program:
VeryComplexQueryExpression.QueryStringsWithRawDelegates();
Если теперь запустить приложение, чтобы опробовать все возможные подходы, вывод окажется идентичным независимо от выбранного пути. Запомните перечисленные ниже моменты относительно выражений запросов и их внутреннего представления.
• Выражения запросов создаются с применением разнообразных операций запросов С# .
• Операции запросов — это просто сокращенное обозначение для вызова расширяющих методов, определенных в типе System.Linq.Enumerable.
• Многие методы класса Enumerable требуют передачи делегатов (в частности, Func<>) в качестве параметров.
• Любой метод, ожидающий параметра типа делегата, может принимать вместо него лямбда-выражение.