Секреты LINQ. Автогенерируемые свойства. Инициализаторы объектов и коллекций, страница 23

Встроенная операция orderby также имеет аналог в виде extension-метода OrderBy. Покажем, как они работают на примере сортировки массива анонимных объектов.

var people = new[]{

new {Name="Alex Black", Position="Developer", Birth=new DateTime(1980,2,26) },

new {Name="Alex Ritter", Position="Boss", Birth=new DateTime(1969,3,15) },

new {Name="Peter Pann", Position="Manager", Birth=new DateTime(1980,9,12) },

new {Name="Joy Amore", Position="Referent", Birth=new DateTime(1990,6,27) },

new {Name="Jack O'Mule", Position="Referent", Birth=new DateTime(1990,4,2) },

new {Name="Jill Sweet", Position="Referent", Birth=new DateTime(1990,9,20) },

new {Name="Brad Show", Position="Developer", Birth=new DateTime(1980,2,26) }

};

var q = from p in people

orderby p.Position descending, p.Name

select p;

foreach (var v in q)

Console.WriteLine(v);

Последовательность объектов сортируется сначала по убыванию поля Position, затем по возрастанию поля Name. Вариант с использованием extension-методов выглядит следующим образом.

q = people.OrderByDescending (p => p.Position).ThenBy (p => p.Name);

Вы видите, что подход с ипользованием extension-методов по краткости не уступает подходу на основе интегрированных запросов, то есть, встроенных операций: from, select, orderby, descending.

Группировка

Встроенные операции: group и by, а также их аналог в виде extension-метода GroupBy, позволяют создать группы объектов. Результат группировки — коллекция коллекций, то есть, последовательность групп, где каждая группа — это последовательность объектов. Каждой группе присваивается ключ (свойство Key). Для перечисления элементов групп нобходимо создать два вложенных цикла. Внешний цикл по группам (различаемым ключами), внутренний — по элементам внутри каждой группы.

var numbers = new [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var gr = from n in numbers group n by n % 3;

Console.WriteLine("Test grouping");

foreach (var g in gr.ToArray()) // Вместо var можно подставить тип IGrouping<int, int>

{

Console.Write("\n Group # " + g.Key + ":  ");

foreach (var v in g.ToArray())

Console.Write(v + ", ");

}

Пример выше разбивает массив целых чисел на три группы в зависимости от остатка деления на 3. Двойной цикл выводит все элементы с учетом деления на группы.

Test grouping

Group # 1:  1, 4, 7, 10,

Group # 2:  2, 5, 8,

Group # 0:  3, 6, 9,

Анализируя код, обратите внимание на свойство Key.

Задание. Попробуйте вычислить тип переменной gr (то есть, тип самой группы).

Ответ. Описатель var можно заменить описателем типа: IEnumerable<IGrouping<int,int>>. Если вывести на консоль имя типа, например: Console.WriteLine(gr.GetType().Name);, то вы увидите внутреннее имя типа: "GroupedEnumerable`3". Этим именем пользуется компилятор и здесь мы видим намек на три параметра. Если в окне отладчика проверить истинность утверждения: gr.GetType() == typeof(System.Linq.IGrouping<int, int, int>), то мы получим true. Но при попытке вставить это выражение в код программы компилятор выдаст ошибку.

Задание. Вычислите тип переменной g. Ответ. IGrouping<int, int>.

Следующий фрагмент реализует тот же самый запрос, но с помощью extension-метода GroupBy.

gr = numbers.GroupBy (n => n % 3);   // Он заметно короче

Теперь покажем, как заменить цикл перебора элементов extension-методом ForEach.

Console.WriteLine("\nTest grouping (Extension)");

Array.ForEach (gr.ToArray(), x =>

{

Console.Write("\n Group # " + x.Key + ":  ");

Array.ForEach (x.ToArray(), y => Console.Write(y + ", "));

});

Обратите внимание на следующие детали:

¨  Перечисление результатов группировки требует вызвать ForEach дважды.

¨  Для прохода вдоль последовательности gr, имеющей тип IEnumerable<IGrouping<int,int>>, необходимо привести ее к типу Array. Это делает extension-метод ToArray.