Язык программирования C#9 и платформа .NET5
{Console.WriteLine(c.ToString());}}Как демонстрировалось ранее в главе, при указании конструкции
разрешено применять любые операции C# для построения сложных выражений. Например, вспомните запрос, который извлекал только автомобили марки BMW, движущиеся со скоростью минимум 90 миль в час:where// Получить автомобили BMW, движущиеся со скоростью минимум 90 миль в час.var onlyFastBMWs =from cin myCarswhere c.Make == "BMW" && c.Speed >= 100select c;Проецирование в новые типы данных
Новые формы данных также можно проецировать из существующего источника данных. Давайте предположим, что необходимо принять входной параметр
и получить результирующий набор, который учитывает только имя и описание каждого товара. Для этого понадобится определить операторProductInfo[], динамически выдающий новый анонимный тип:selectstatic void GetNamesAndDescriptions(ProductInfo[] products){Console.WriteLine("Names and Descriptions:");var nameDesc =from pin productsselect new { p.Name, p.Description };foreach (var item in nameDesc){// Можно было бы также использовать свойства Name// и Description напрямую.Console.WriteLine(item.ToString());}}Не забывайте, что когда запрос LINQ использует проекцию, нет никакого способа узнать лежащий в ее основе тип данных, т.к. он определяется на этапе компиляции. В подобных случаях ключевое слово
является обязательным. Кроме того, вспомните о невозможности создания методов с неявно типизированными возвращаемыми значениями. Таким образом, следующий метод не скомпилируется:varstatic var GetProjectedSubset(ProductInfo[] products){var nameDesc =from p in products select new { p.Name, p.Description };return nameDesc; // Так поступать нельзя!}В случае необходимости возвращения спроецированных данных вызывающему коду один из подходов предусматривает трансформацию результата запроса в объект
с применением расширяющего методаSystem.Array. Следовательно, модифицировав выражение запроса, как показано ниже:ТоArray()// Теперь возвращаемым значением является объект Array.static Array GetProjectedSubset(ProductInfo[] products){var nameDesc =from p in products select new { p.Name, p.Description };// Отобразить набор анонимных объектов на объект Array.return nameDesc.ToArray();}метод
можно вызвать и обработать возвращенные им данные:GetProjectedSubset()Array objs = GetProjectedSubset(itemsInStock);foreach (object o in objs){Console.WriteLine(o); // Вызывает метод ToString()// на каждом анонимном объекте.}Как видите, здесь должен использоваться буквальный объект
, а применять синтаксис объявления массива C# невозможно, учитывая, что лежащий в основе проекции тип неизвестен, поскольку речь идет об анонимном классе, который сгенерирован компилятором. Кроме того, параметр типа для обобщенного методаSystem.Arrayне указывается, потому что он тоже не известен вплоть до этапа компиляции.ToArray<Т>()Очевидная проблема связана с утратой строгой типизации, т.к. каждый элемент в объекте
считается относящимся к типуArray. Тем не менее, когда нужно возвратить результирующий набор LINQ, который является результатом операции проецирования в анонимный тип, трансформация данных в типObject(или другой подходящий контейнер через другие члены типаArray) обязательна.EnumerableПроецирование в другие типы данных
В дополнение к проецированию в анонимные типы результаты запроса LINQ можно проецировать в другой конкретный тип, что позволяет применять статическую типизацию и реализацию
как результирующий набор. Для начала создайте уменьшенную версию классаIEnumerable<T>:ProductInfonamespace FunWithLinqExpressions{class ProductInfoSmall{public string Name {get; set;} = "";public string Description {get; set;} = "";public override string ToString()=> $"Name={Name}, Description={Description}";}}Следующее изменение касается проецирования результатов запроса в коллекцию объектов
, а не анонимных типов. Добавьте в классProductInfoSmallследующий метод:ProductInfoSmall