Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 169 170 171 172 173 174 175 176 177 ... 407
Вот базовый шаблон, который нужно соблюдать:

var результат =

  from сопоставляемыйЭлемент in контейнер

  select сопоставляемыйЭлемент;

Элемент после операции from представляет элемент, соответствующий критерию запроса LINQ; именовать его можно по своему усмотрению. Элемент после операции in представляет контейнер данных, в котором производится поиск (массив, коллекция, документ XML и т.д.).

Рассмотрим простой запрос, не делающий ничего кроме извлечения каждого элемента контейнера (по поведению похожий на SQL-оператор SELECT * в базе данных):

static void SelectEverything(ProductInfo[] products)

{

  // Получить все!

  Console.WriteLine("All product details:");

  var allProducts = from p in products select p;

  foreach (var prod in allProducts)

  {

    Console.WriteLine(prod.ToString());

  }

}

По правде говоря, это выражение запроса не особенно полезно, т.к. оно выдает подмножество, идентичное содержимому входного параметра. При желании можно извлечь только значения Name каждого товара, применив следующий синтаксис выборки:

static void ListProductNames(ProductInfo[] products)

{

  // Теперь получить только наименования товаров.

  Console.WriteLine("Only product names:");

  var names = from p in products select p.Name;

  foreach (var n in names)

   {

    Console.WriteLine("Name: {0}", n);

  }

}

Получение подмножества данных

Чтобы получить определенное подмножество из контейнера, можно использовать операцию where. Общий шаблон запроса становится таким:

var результат =

  from элемент in контейнер

  where булевскоеВыражение

 select элемент;

Обратите внимание, что операция where ожидает выражение, результатом вычисления которого является булевское значение. Например, чтобы извлечь из аргумента ProductInfo[] только товарные позиции, складские запасы которых составляют более 25 единиц, можно написать следующий код:

static void GetOverstock(ProductInfo[] products)

{

  Console.WriteLine("The overstock items!");

  // Получить только товары со складским запасом более 25 единиц.

  var overstock =

    from p

    in products

    where p.NumberInStock > 25

    select p;

  foreach (ProductInfo c in overstock)

  {

    Console.WriteLine(c.ToString());

  }

}

Как демонстрировалось ранее в главе, при указании конструкции where разрешено применять любые операции C# для построения сложных выражений. Например, вспомните запрос, который извлекал только автомобили марки BMW, движущиеся со скоростью минимум 90 миль в час:

// Получить автомобили BMW, движущиеся со скоростью минимум 90 миль в час.

var onlyFastBMWs =

  from c

  in myCars

  where c.Make == "BMW" && c.Speed >= 100

  select c;

Проецирование в новые типы данных

Новые формы данных также можно проецировать из существующего источника данных. Давайте предположим, что необходимо принять входной параметр ProductInfo[] и получить результирующий набор, который учитывает только имя и описание каждого товара. Для этого понадобится определить оператор select, динамически выдающий новый анонимный тип:

static void GetNamesAndDescriptions(ProductInfo[] products)

{

  Console.WriteLine("Names and Descriptions:");

  var nameDesc =

    from p

    in products

    select new { p.Name, p.Description };

  foreach (var item in nameDesc)

  {

    // Можно было бы также использовать свойства Name

    // и Description напрямую.

    Console.WriteLine(item.ToString());

  }

}

Не забывайте, что когда запрос LINQ использует проекцию, нет никакого способа узнать лежащий в ее основе тип данных, т.к. он определяется на этапе компиляции. В подобных случаях ключевое слово var является обязательным. Кроме того, вспомните о невозможности создания методов с неявно типизированными возвращаемыми значениями. Таким образом, следующий метод не скомпилируется:

static 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()

                        // на каждом анонимном объекте.

}

Как видите, здесь должен использоваться буквальный объект System.Array, а применять синтаксис объявления массива C# невозможно, учитывая, что лежащий в основе проекции тип неизвестен, поскольку речь идет об анонимном классе, который сгенерирован компилятором. Кроме того, параметр типа для обобщенного метода ToArray<Т>() не указывается, потому что он тоже не известен вплоть до этапа компиляции.

Очевидная проблема связана с утратой строгой типизации, т.к. каждый элемент в объекте Array считается относящимся к типу Object. Тем не менее, когда нужно возвратить результирующий набор LINQ, который является результатом операции проецирования в анонимный тип, трансформация данных в тип Array (или другой подходящий контейнер через другие члены типа Enumerable) обязательна.

Проецирование в другие типы данных

В дополнение к проецированию в анонимные типы результаты запроса LINQ можно проецировать в другой конкретный тип, что позволяет применять статическую типизацию и реализацию IEnumerable<T> как результирующий набор. Для начала создайте уменьшенную версию класса ProductInfo:

namespace FunWithLinqExpressions

{

  class ProductInfoSmall

  {

    public string Name {get; set;} = "";

    public string Description {get; set;} = "";

    public override string ToString()

      => $"Name={Name}, Description={Description}";

  }

}

Следующее изменение касается проецирования результатов запроса в коллекцию объектов ProductInfoSmall, а не анонимных типов. Добавьте в класс ProductInfoSmall следующий метод:

static void GetNamesAndDescriptionsTyped(

  ProductInfo[] products)

{

1 ... 169 170 171 172 173 174 175 176 177 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий