Отношения между типами в операциях запросов LINQ (C#)

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

Операции запросов LINQ строго типизированы в источнике данных, в самом запросе и при выполнении запроса. Тип переменных в запросе должен быть совместим с типом элементов в источнике данных и с типом переменной итерации в операторе foreach. Строгая типизация гарантирует перехват ошибок во время компиляции, когда их можно будет исправить прежде, чем с ними столкнутся пользователи.

Для демонстрации связи типов большая часть примеров использует явную типизацию для всех переменных. Последний пример показывает применение тех же принципов даже при использовании неявной типизации с помощью var.

Запросы, не выполняющие преобразование исходных данных

На следующем рисунке показана операция запроса LINQ to Objects, не выполняющая преобразование данных. Источник содержит последовательность строк, результат запроса также является последовательностью строк.

Схема, показывающая отношения между типами данных в запросе LINQ.

  1. Аргумент типа источника данных определяет тип переменной диапазона.

  2. Тип выбранного объекта определяет тип переменной запроса. Здесь name является строкой. Следовательно, переменная запроса представляет собой IEnumerable<string>.

  3. Итерация переменной запроса выполняется в операторе foreach. Поскольку переменная запроса является последовательностью строк, переменная итерации также является строкой.

Запросы, выполняющие преобразование исходных данных

На следующем рисунке показана операция запроса LINQ to SQL, которая выполняет простое преобразование данных. В качестве входных данных запрос получает последовательность объектов Customer и выбирает в результате только свойство Name. Поскольку Name является строкой, запрос формирует последовательность строк в качестве выходных данных.

Схема, показывающая запрос, преобразующий тип данных.

  1. Аргумент типа источника данных определяет тип переменной диапазона.

  2. Оператор select возвращает свойство Name вместо целого объекта Customer. Поскольку Name является строкой, аргумент типа custNameQuery является string, а не Customer.

  3. Поскольку custNameQuery представляет собой последовательность строк, переменная итерации цикла foreach также должна быть string.

На следующем рисунке показано немного более сложное преобразование. Оператор select возвращает анонимный тип, захватывающий только два члена исходного объекта Customer.

Схема, показывающая более сложный запрос, преобразующий тип данных.

  1. Аргумент типа источника данных всегда является типом переменной диапазона в запросе.

  2. Так как оператор select создает анонимный тип, переменная запроса должна неявно типизирована с помощью var.

  3. Поскольку тип переменной запроса неявный, переменная итерации в цикле foreach также должна быть неявной.

Разрешение компилятору определять сведения о типе

Несмотря на то, что необходимо обладать знаниями об отношениях типов в операции запроса, существует возможность передачи выполнения всех действий компилятору. Ключевое слово var можно использовать для любой локальной переменной в операции запроса. Следующий рисунок похож на пример 2, рассмотренный выше. Однако компилятор предоставляет строгий тип для каждой переменной в операции запроса.

Схема, показывающая поток для типа с неявной типизацией.

Дополнительные сведения о var см. в разделе Неявно типизированные локальные переменные.