¨ В первом варианте примера мы намеренно не пользуемся λ-выражением с целью показать, как в действительности работает внутренняя кухня LINQ.
¨ Во втором варианте, наоборот, включаются все средства минимизации выражений, присущие LINQ.
Как было сказано ранее, компилятор заменяет λ-выражение кодом, который вызывает функцию. Эта функция соответствует делегатному типу Func<>. Код первого варианта выполняет это действие явным образом. Итак, для реализации первого варианта надо:
¨ создать метод, соответствующий делегатному типу Func<T, TResult>,
¨ создать последовательность объектов типа T,
¨ создать LINQ-запрос, вызвав, например, extension-метод Where (Func<T, TResult>),
¨ создать перечислитель IEnumerable<T>, настроенный на выбранный ранее тип,
¨ перечислить выходную последовательность, генерируемую при выполнении отложенного запроса.
Следующий кодовый фрагмент реализует намеченный план. В качестве предиката, который следует задать при вызове метода Where, мы используем метод IsLeapYear, проверяющий целое число на соответствие високосным годам.
static bool IsLeapYear(int y)
{
bool res = y % 4 == 0 && y % 100 != 0 || y % 1000 == 0;
return res;
}
static void TestLeapYear()
{
Console.WriteLine("\nTest Verbose version of IsLeapYear Query");
var years = new int[] { 2000, 2001, 2002, 2003, 2004, 2005 };
Func<int, bool> tester = IsLeapYear;
IEnumerable<int> result = years.Where<int>(tester);
IEnumerator<int> en = result.GetEnumerator();
while (en.MoveNext())
Console.WriteLine(en.Current);
}
Теперь рассмотрим код второго варианта этого же запроса. Он использует операции from, where и select, встроенные в язык C# 3.5. С их помощью реализуются интегрированные запросы.
Console.WriteLine("\nConcise Version of IsLeapYear Query");
var q = from y in Enumerable.Range(2000, 6)
where (y % 4 == 0 && y % 100 != 0 || y % 1000 == 0)
select y;
foreach (var v in q)
Console.WriteLine(v);
Этот запрос реализует тот же самый алгоритм — отбирает из массива целых чисел только те, которые соответствуют високосным годам. Согласитесь, что второй вариант намного проще создать, прочесть и при желании модифицировать. Интегрированные запросы дружелюбны, но компилятор превращает их в вызовы extension-методов. C некоторыми допущениями можно сказать, что компилятр языка C# превращает второй вариант запроса в первый.
Синтаксис оператора from x in коллекция выстроен так, чтобы задать переменную x для прохода вдоль всей последовательности (или коллекции). Эту переменную называют псевдонимом (alias). Заметим, что оператор from не имеет соответствующего extension-метода.
Вы заметили, что, в отличие от обычных SQL-запросов, операторы select и from стоят не на своих местах, но благодаря такому решению, появляется возможность включить механизм Intellisense. Как только стала известна коллекция (после предложения from x in коллекция), начинают работать метаданные, и, включаются подсказки. Такой подход значительно упрощает и ускоряет процесс разработки приложения.
Рассмотрим еще два варианта выполнения одного и того же запроса. Первый — использует extension-метод Select, а второй — встроенную операцию select. На сей раз запрос проецирует диапазон целых чисел на множество анонимных объектов. Вот код первого варианта:
var q = Enumerable.Range(20,10).Select(n => new { Value = n, Mult3 = n % 3 == 0 });
Заметьте, что здесь необходимо использовать λ-операцию =>. Теперь рассмотрим второй вариант запроса, который обходится без λ-операции, но требует дополнить select двумя другими операциями (from и in).
q = from n in Enumerable.Range(20, 10)
select new { Value = n, Mult3 = n % 3 == 0 };
Для вывода результатов запросов воспользуемся уже опробованным приемом.
Console.WriteLine("\nNumber Mult3?\r\n" + new string('\u2500', 21));
foreach (var v in q)
Console.WriteLine("{0,-10} {1}", v.Value, v.Mult3);
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.