- •А.А. Волосевич
- •Содержание
- •1. Работа с числами
- •2. Представление даты и времени
- •3. Работа со строками и текстом
- •4. Преобразование информации
- •5. Сравнение для выяснения равенства
- •6. Сравнение для выяснения порядка
- •7. Жизненный цикл объектов
- •7.1. Алгоритм сборки мусора
- •7.2. Финализаторы и интерфейс iDisposable
- •7.3. Слабые ссылки
- •8. Перечислители и итераторы
- •9. Стандартные интерфейсы коллекций
- •10. Массивы и класс System.Array
- •11. Типы для работы с коллекциями-списками
- •12. Типы для работы с коллекциями-множествами
- •13. Типы для работы с коллекциями-словарями
- •14. Типы для создания пользовательских коллекций
- •15. Технология linq to Objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •16. Работа с объектами файловой системы
- •17. Ввод и вывод информации
- •17.1. Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •17.2. Адаптеры потоков
- •18. Основы xml
- •19. Технология linq to xml
- •20. Дополнительные возможности обработки xml
- •21. Сериализация времени выполнения
- •22. Контракты данных и xml-сериализация
- •23. Состав и взаимодействие сборок
- •24. Метаданные и получение информации о типах
- •25. Позднее связывание и кодогенерация
- •26. Атрибуты
- •27. Динамическое связывание
- •28. Файлы конфигурации
- •29. Диагностика и мониторинг
- •30. Процессы и домены
- •31. Основы многопоточного программирования
- •32. Синхронизация потоков
- •32.1. Критические секции
- •32.2. Синхронизация на основе подачи сигналов
- •32.3. Неблокирующие средства синхронизации
- •32.4. Разделение данных между потоками
- •33. Библиотека параллельных задач
- •33.1. Параллелизм на уровне задач
- •33.2. Параллелизм при императивной обработке данных
- •33.3. Параллелизм при декларативной обработке данных
- •33.4. Обработка исключений и отмена выполнения задач
- •33.5. Коллекции, поддерживающие параллелизм
- •34. Асинхронный вызов методов
- •Литература
3. Операторы упорядочивания.
Операторы OrderBy() и OrderByDescending() выполняют сортировку коллекции по возрастанию или убыванию соответственно. Имеется версия данных операторов, принимающая в качестве дополнительного аргумента объект, реализующий IComparer<T>.
IOrderedEnumerable<T> OrderBy<T, K>(this IEnumerable<T> source,
Func<T, K> keySelector);
IOrderedEnumerable<T> OrderByDescending<T, K>(
this IEnumerable<T> source, Func<T, K> keySelector);
Интерфейс IOrderedEnumerable<T> является наследником IEnumerable<T> и описывает упорядоченную последовательность элементов с указанием на ключ сортировки. Если после выполнения сортировки по одному ключу требуется дополнительная сортировка по другому ключу, нужно воспользоваться операторами ThenBy() и ThenByDescending(). Имеется также оператор Reverse(), обращающий коллекцию.
Пример использования операторов упорядочивания:
var r1 = Enumerable.Reverse(gr);
var r2 = gr.OrderBy(student => student.Age);
var r3 = gr.OrderByDescending(student => student.Age)
.ThenBy(student => student.Name);
Чтобы получить коллекцию r1, метод расширения использовался как обычный статический метод класса, так как у List<T> имеется собственный метод Reverse(). В коллекции r3 студенты упорядочены по убыванию возраста, а при совпадении возрастов – по фамилиям в алфавитном порядке.
4. Оператор группировки GroupBy().
Оператор группировки GroupBy() разбивает коллекцию на группы элементов с одинаковым значением некоторого ключа. Оператор GroupBy() имеет перегруженные версии, позволяющие указать селектор ключа, преобразователь элементов в группе, объект, реализующий IEqualityComparer<T> для сравнения ключей. Простейшая версия оператора группировки имеет следующий вид:
IEnumerable<IGrouping<K, T>> GroupBy<T, K>(this IEnumerable<T> src,
Func<T, K> keySelector);
Здесь последний параметр указывает на функцию, которая строит по элементу поле-ключ. Обычно эта функция просто выбирает одно из полей объекта. Интерфейс IGrouping<K, T> унаследован от IEnumerable<T> и содержит дополнительное типизированное свойство Key – ключ группировки.
Рассмотрим применение оператора группировки. Сгруппируем студентов по возрасту и для каждой группы возрастов выведем ключ и элементы:
var r1 = gr.GroupBy(student => student.Age);
foreach (IGrouping<int, Student> group in r1)
{
Console.WriteLine(group.Key);
foreach (Student student in group)
{
Console.WriteLine(student.Name);
}
}
5. Операторы соединения.
Операторы соединения применяются, когда требуется соединить две коллекции, элементы которых имеют общие атрибуты. Основным оператором соединения является оператор Join().
IEnumerable<V> Join<T, U, K, V>(this IEnumerable<T> outer,
IEnumerable<U> inner,
Func<T, K> outerKeySelector,
Func<U, K> innerKeySelector,
Func<T, U, V> resultSelector);
Оператор Join() требует задания двух коллекций (внешней и внутренней) и трёх функций. Первая функция порождает ключ из элемента внешней коллекции, вторая – из элемента внутренней коллекции, а третья функция продуцирует объект коллекции-результата. При выполнении соединения Join() итерируется по внешней коллекции и ищет соответствия с элементами внутренней коллекции. При этом возможны следующие ситуации:
1. Найдено одно соответствие – в результат включается один элемент.
2. Найдено множественное соответствие – результат содержит по элементу для каждого соответствия.
3. Соответствий не найдено – элемент не входит в результат.
Рассмотрим примеры использования оператора Join(). Для этого опишем коллекцию cit объектов класса Citizen.
public class Citizen
{
public int BirthYear { get; set; }
public string IDNumber { get; set; }
}
int year = DateTime.Now.Year;
var cit = new List<Citizen> {
new Citizen {BirthYear = year - 17, IDNumber = "KLM897"},
new Citizen {BirthYear = year - 18, IDNumber = "WEF442"},
new Citizen {BirthYear = year - 18, IDNumber = "HHH888"},
new Citizen {BirthYear = year - 25, IDNumber = "XYZ012"}};
Выполним оператор Join():
var r1 = gr.Join(cit,
student => year - student.Age,
citizen => citizen.BirthYear,
(student, citizen) => new {student.Name, citizen.IDNumber});
// r1 содержит следующие объекты:
// {Name = "Smirnov", IDNumber = "WEF442"}
// {Name = "Smirnov", IDNumber = "HHH888"}
// {Name = "Kuznetsov", IDNumber = "WEF442"}
// {Name = "Kuznetsov", IDNumber = "HHH888"}
Оператор GroupJoin() порождает набор, группируя элементы внутренней коллекции при нахождении соответствия с элементом внешней коллекции: Если же соответствие не найдено, в результат включается пустая группа.
IEnumerable<V> GroupJoin()<T, U, K, V>(this IEnumerable<T> outer,
IEnumerable<U> inner,
Func<T, K> outerKeySelector,
Func<U, K> innerKeySelector,
Func<T, IEnumerable<U>, V> resultSelector);
Ниже приведён пример использования GroupJoin().
var r3 = cit.GroupJoin(gr,
citizen => citizen.BirthYear,
student => year - student.Age,
(citizen, group) => new {citizen.IDNumber,
Names = group.Select(st => st.Name)});
foreach (var data in r3)
{
Console.WriteLine(data.IDNumber);
foreach (string s in data.Names)
{ // 1-я и 4-я группы пусты,
Console.WriteLine(s); // 2-я и 3-я содержат по два элемента
}
}
Оператор Zip() порождает набор на основе двух исходных коллекций, выполняя заданное генерирование элементов. Длина результирующей коллекции равна длине меньшей из двух исходных коллекций.
IEnumerable<V> Zip<T, U, V>(this IEnumerable<T> first,
IEnumerable<U> second,
Func<T, U, V> resultSelector);
Дадим простейший пример использования Zip():
int[] numbers = {1, 2, 3, 4};
string[] words = {"one", "two", "three"};
var res = numbers.Zip(words, (f, s) => f + "=" + s); // 3 элемента