Язык программирования C#9 и платформа .NET5
System.Collections.IEnumerable subset =from i in numberswhere i < 10select i;К счастью, неявная типизация при работе с запросами LINQ значительно проясняет картину:
static void QueryOverInts(){int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};<b> // Здесь используется неявная типизация...</b>var subset = from i in numbers where i < 10 select i;<b> // ...и здесь тоже.</b>foreach (var i in subset){Console.WriteLine("Item: {0} ", i);}ReflectOverQueryResults(subset);}В качестве эмпирического правила: при захвате результатов запроса LINQ всегда необходимо использовать неявную типизацию. Однако помните, что (в большинстве случаев) действительное возвращаемое значение имеет тип, реализующий интерфейс
.IEnumerable<T>Какой точно тип кроется за ним (
,OrderedEnumerable<TElement, ТКеу>и т.п.), к делу не относится, и определять его вовсе не обязательно. Как было показано в предыдущем примере кода, для прохода по извлеченным данным можно просто применить ключевое слово var внутри конструкцииWhereArrayIterator<T>.foreachLINQ и расширяющие методы
Несмотря на то что в текущем примере совершенно не требуется напрямую писать какие-то расширяющие методы, на самом деле они благополучно используются на заднем плане. Выражения запросов LINQ могут применяться для прохода по содержимому контейнеров данных, которые реализуют обобщенный интерфейс
. Тем не менее, классIEnumerable<T>(используемый для представления массива строк и массива целых чисел) не реализует этот контракт:System.Array// Похоже, что тип System.Array не реализует// корректную инфраструктуру для выражений запросов!public abstract class Array : ICloneable, IList,IStructuralComparable, IStructuralEquatable{...}Хотя класс
не реализует напрямую интерфейсSystem.Array, он косвенно получает необходимую функциональность данного типа (а также многие другие члены, связанные с LINQ) через статический тип классаIEnumerable<T>.System.Linq.EnumerableВ служебном классе
определено множество обобщенных расширяющих методов (таких какSystem.Linq.Enumerable,Aggregate<T>(),First<T>()и т.д.), которые классМах<Т>()(и другие типы) получают в свое распоряжение на заднем плане. Таким образом, если вы примените операцию точки к локальной переменнойSystem.Array, то обнаружите большое количество членов, которые отсутствуют в формальном определенииcurrentVideoGames.System.ArrayРоль отложенного выполнения
Еще один важный момент, касающийся выражений запросов LINQ, заключается в том, что фактически они не оцениваются до тех пор, пока не начнется итерация по результирующей последовательности. Формально это называется отложенным выполнением. Преимущество такого подхода связано с возможностью применения одного и того же запроса LINQ многократно к тому же самому контейнеру и полной гарантией получения актуальных результатов. Взгляните на следующее обновление метода
:QueryOverlnts()static void QueryOverInts(){int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };// Получить числа меньше 10.var subset = from i in numbers where i < 10 select i;<b> // Оператор LINQ здесь оценивается!</b>foreach (var i in subset){Console.WriteLine("{0} < 10", i);}Console.WriteLine();// Изменить некоторые данные в массиве.numbers[0] = 4;<b> // Снова производится оценка!</b>foreach (var j in subset){Console.WriteLine("{0} < 10", j);}Console.WriteLine();ReflectOverQueryResults(subset);}На заметку! Когда оператор LINQ выбирает одиночный элемент (с использованием
,First()/FirstOrDefault()или любого метода агрегирования), запрос выполняется немедленно. МетодыSingle()/SingleOrDefault(),First(),FirstOrDefault()иSingle()будут описаны в следующем разделе. Методы агрегирования раскрываются позже в главе.SingleOrDefaultНиже показан вывод, полученный в результате запуска программы. Обратите внимание, что во второй итерации по запрошенной последовательности появился дополнительный член, т.к. для первого элемента массива было установлено значение меньше 10: