Язык программирования C#9 и платформа .NET5
Модифицируйте код метода
следующим образом:QueryOverStrings()// Построить выражение запроса для поиска// в массиве элементов, содержащих пробел.IEnumerable<string> subset =from g in currentVideoGameswhere g.Contains(" ")orderby gselect g;<b>ReflectOverQueryResults(subset);</b>// Вывести результаты.foreach (string s in subset){Console.WriteLine("Item: {0}", s);}Запустив приложение, легко заметить, что переменная
в действительности представляет собой экземпляр обобщенного типаsubset(представленного в коде CIL какOrderedEnumerable<TElement, ТКеу>), который является внутренним абстрактным типом, находящимся в сборкеOrderedEnumerable`2:System.Linq.dll***** Info about your query using Query Expressions*****resultSet is of type: OrderedEnumerable`2resultSet location: System.LinqВнесите такое же изменение в код метода
, но передав во втором параметре строку "QueryOverStringsWithExtensionMethods()":Extension Methods// Построить выражение запроса для поиска// в массиве элементов, содержащих пробел.IEnumerable<string> subset =currentVideoGames.Where(g => g.Contains(" ")).OrderBy(g => g).Select(g => g);<b>ReflectOverQueryResults(subset,"Extension Methods");</b>// Вывести результаты.foreach (string s in subset){Console.WriteLine("Item: {0}", s);}После запуска приложения выяснится, что переменная
является экземпляром типаsubset. Но если удалить из запроса конструкциюSelectIPartitionIterator, тоSelect(g=>g)снова станет экземпляром типаsubset. Что все это значит? Для подавляющего большинства разработчиков немногое (если вообще что-либо). Оба типа являются производными отOrderedEnumerable<TElement, ТКеу>, проход по ним осуществляется одинаковым образом и они оба способны создавать список или массив своих значений.IEnumerable<T>***** Info about your query using Extension Methods *****resultSet is of type: SelectIPartitionIterator`2resultSet location: System.LinqLINQ и неявно типизированные локальные переменные
Хотя в приведенной программе относительно легко выяснить, что результирующий набор может быть интерпретирован как перечисление объектов
(например,string), тот факт, что подмножество на самом деле имеет типIEnumerable<string>, не настолько ясен.OrderedEnumerable<TElement, ТКеу>Поскольку результирующие наборы LINQ могут быть представлены с применением порядочного количества типов из разнообразных пространств имен LINQ, было бы утомительно определять подходящий тип для хранения результирующего набора. Причина в том, что во многих случаях лежащий в основе тип не очевиден и даже напрямую не доступен в коде (и как вы увидите, в ряде ситуаций тип генерируется на этапе компиляции).
Чтобы еще больше подчеркнуть данное обстоятельство, ниже показан дополнительный вспомогательный метод, определенный внутри класса
:Programstatic void QueryOverInts(){int[] numbers = {10, 20, 30, 40, 1, 2, 3, 8};// Вывести только элементы меньше 10.IEnumerable<int> subset = from i in numbers where i < 10 select i;foreach (int i in subset){Console.WriteLine("Item: {0}", i);}ReflectOverQueryResults(subset);}В рассматриваемом случае переменная
имеет совершенно другой внутренний тип. На этот раз тип, реализующий интерфейсsubset, представляет собой низкоуровневый класс по имениIEnumerable<int>:WhereArrayIterator<T>Item: 1Item: 2Item: 3Item: 8***** Info about your query *****resultSet is of type: WhereArrayIterator`1resultSet location: System.LinqУчитывая, что точный тип запроса LINQ не вполне очевиден, в первых примерах результаты запросов были представлены как переменная
, гдеIEnumerable<T>— тип данных в возвращенной последовательности (Т,stringи т.д.). Тем не менее, ситуация по-прежнему довольно запутана. Чтобы еще больше все усложнить, стоит упомянуть, что поскольку интерфейсintрасширяет необобщенныйIEnumerable<T>, получать результат запроса LINQ допускается и так:IEnumerable