Класс Enumerable LINQ - Enumerable изнутри

ОГЛАВЛЕНИЕ

Enumerable изнутри

Класс Enumerable играет важную роль в каждом создаваемом запросе LINQ. Например, представьте, что в ваш проект входит класс Product и общие экземпляры List of Product (как в примере проекта). Вы создали на основе примера экземпляр класса SimpleDataContext, который наполняет данными список Products, и пишете запрос, подобный следующему.

Dim db As New SimpleDataContext()
Dim query = _
  From prod In db.Products _
  Where prod.CategoryID = 1 _
  Order By prod.ProductName _
  Select prod.ProductName

Если вы долго размышляли над этим, вероятно, вы осознали, что компилятор Visual Basic должен «за кадром» преобразовать синтаксис типа SQL в некоторый набор вызовов методов из некоторого экземпляра подобного типа. Действительно, в этом примере свойство Products класса SimpleDataContext имеет тип List(Of Product), и, как вы, возможно, догадались, тип List(Of T) реализует интерфейс IEnumerable(Of T). Компилятор преобразует ключевые слова LINQ в соответствующие вызовы методов расширения из класса типа Enumerable. А именно, можно создать в точности такой же набор команд, переписав запрос LINQ следующим образом.

Dim query = _
  db.Products _
  .Where(Function(prod) CBool(prod.CategoryID = 1)) _
  .OrderBy(Function(prod) prod.ProductName) _
  .Select(Function(prod) prod.ProductName)

Если изучить промежуточный язык (IL), созданный компилятором, с помощью запроса LINQ, использующего ключевые слова Visual Basic, будет видно, что компилятор преобразует ключевые слова Visual Basic в вызовы соответствующих методов класса Enumerable. В результате расширяется поведение класса System.Collections.Generic.List(Of T). (По существу, это легко определить — загляните в документацию по членам этого конкретного класса, и вы увидите список методов расширения, большая часть которых определяется классом Enumerable.)

Что все это означает для вас? Поскольку методы расширения класса Enumerable могут обрабатывать многие другие классы, включая Array и List, есть возможность использовать методы класса Enumerable не только для создания запросов LINQ, но также для манипулирования поведением массивов и других структур данных.

Если изучить документацию по методам расширения класса Enumerable, выяснится, что все члены являются общими (Shared). В итоге у вас есть два варианта вызова метода. Во-первых, его можно вызывать как метод декларативного типа, передавая экземпляр расширенного класса (в предположении, что переменная с результатами имеет тип, который может быть обработан методом Count), следующим образом.

Dim itemCount = _
  Enumerable.Count(results, _
  Function(prod) prod.CategoryID = 3)

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

Dim itemCount = _
  results.Count(Function(prod) _
  prod.CategoryID = 3)

Visual Basic и C# по-разному выполняют преобразование ключевых слов в вызовы методов расширения. Visual Basic предоставляет большее, чем C#, число ключевых слов, которые можно использовать в запросах LINQ. Например, с помощью ключевого слова Take можно создать запрос LINQ в Visual Basic следующим образом.

Dim db As New SimpleDataContext()
Dim query = _
  From prod In db.Products _
  Where prod.CategoryID = 1 _
  Order By prod.ProductName _
  Take 10 _
  Select prod.ProductName

Можно также использовать метод расширения Take, предоставляемый классом Enumerable, следующим образом.

Dim query = _
  (From prod In db.Products _
  Where prod.CategoryID = 1 _
  Order By prod.ProductName _
  Select prod.ProductName).Take(10)

Поскольку C# не предоставляет ключевого слова Take, не остается другого выбора, кроме как явно вызвать метод Take. На рис. 1 представлено соответствие ключевых слов запросов LINQ в Visual Basic и соответствующих методов класса Enumerable.

Рис. 1 Связь ключевых слов LINQ и методов класса Enumerable

Ключевое слово запроса LINQ в Visual BasicМетод класса Enumerable
Aggregate … Into … AllAll
Aggregate … Into … AllAny
Aggregate … Into … AverageAverage
Aggregate … Into … CountCount
Aggregate … Into … LongCountLongCount
Aggregate … Into … MaxMax
Aggregate … Into … MinMin
Aggregate … Into … SumSum
DistinctDistinct
Group ByGroupBy
Group JoinGroupJoin
Order ByOrderBy
Order By … DescendingOrderByDescending
Order By (с несколькими полями)ThenBy
Order By … Descending (с несколькими полями)ThenByDescending
SelectSelect
SkipSkip
TakeTake
Take WhileTakeWhile
WhereWhere