Курс лекций для студентов специальности i-31 03 04 Информатика всех форм обучения Минск 2010



страница10/29
Дата09.08.2019
Размер0.64 Mb.
#126834
ТипКурс лекций
1   ...   6   7   8   9   10   11   12   13   ...   29

2.14. ТЕХНОЛОГИЯ LINQ TO OBJECTS


Платформа .NET версии 3.5 представила новую технологию работы с коллекциями  Language Integrated Query (LINQ). По типу обрабатываемой информации LINQ делится на LINQ to Objects – библиотеки для обработки коллекций объектов в памяти; LINQ to SQL – библиотеки для работы с базами данных; LINQ to XML предназначена для обработки XML-информации. В данном параграфе акцент сделан LINQ to Objects.

Технически, LINQ to Objects – это набор классов, содержащих типичные методы обработки коллекций: поиск данных, сортировка, фильтрация. Ядром LINQ to Objects является статический класс Enumerable, размещенный в пространстве имен System.Linq1. Этот класс содержит набор методов расширения интерфейса IEnumerable, которые в дальнейшем будут называться операторами LINQ. Для удобства дальнейшего изложения используем стандартное деление операторов LINQ на группы в зависимости от выполняемых действий:



  1. Оператор условия Where (отложенные вычисления).

  2. Операторы проекций (отложенные вычисления).

  3. Операторы упорядочивания (отложенные вычисления).

  4. Оператор группировки GroupBy (отложенные вычисления).

  5. Операторы соединения (отложенные вычисления).

  6. Операторы работы с множествами (отложенные вычисления).

  7. Операторы агрегирования.

  8. Операторы генерирования (отложенные вычисления).

  9. Операторы кванторов и сравнения.

  10. Операторы разбиения (отложенные вычисления).

  11. Операторы элемента.

  12. Операторы преобразования.

В примерах параграфа будут использоваться либо коллекции примитивных типов, либо коллекция gr объектов класса Student:

public class Student

{

public string Name { get; set; }



public int Age { get; set; }

public IEnumerable Marks { get; set; }

}
var gr = new List{

new Student {Name = "Smirnov", Age = 18, Marks = new[] {10, 8, 9}},

new Student {Name = "Ivanova", Age = 20, Marks = new[] {5, 6, 9}},

new Student {Name = "Kuznetsov", Age = 18, Marks = new[] {7, 7, 4}},

new Student {Name = "Sokolov", Age = 20, Marks = new[] {7, 8, 8}},

new Student {Name = "Lebedeva", Age = 20, Marks = new[] {9, 9, 9}}};



1. Оператор условия Where().

Оператор производит фильтрацию коллекции, основываясь на параметре-предикате. Сигнатуры оператора1:

IEnumerable Where(this IEnumerable source,

Func predicate);


IEnumerable Where(this IEnumerable source,

Func predicate);

Второй вариант оператора Where() позволяет указать индекс, начиная с которого будет применяться предикат (заметим, что многие другие операторы имеют перегруженную версию, устроенную по такому же принципу).

Примеры использования Where():

List lst = new List { 1, 3, -1, -4, 7 };

var r1 = lst.Where(x => x < 0);


var r2 = gr.Where(s => s.Age > 19);

var r3 = gr.Where((s, pos) => s.Age > 19 && pos < 3);



2. Операторы проекций.

Операторы проекций применяются для выборки информации, при этом они могут изменять тип элементов итоговой коллекции. Основным оператором проекции является Select():

IEnumerable Select(this IEnumerable source,

Func selector);

Оператор SelectMany() может применяться в том случае, если результатом проекции является набор данных. В этом случае оператор соединяет все элементы набора в одну коллекцию.

IEnumerable SelectMany(this IEnumerablesource,

Func> selector);

Примеры использования операторов проекций:

IEnumerable r1 = gr.Select(s => s.Name);

var r2 = gr.Select(s => new {s.Name, s.Age});

IEnumerable r3 = gr.SelectMany(s => s.Marks);

Коллекция r1 будет содержать имена студентов. Коллекция r2 состоит из объектов анонимного типа с полями Name и Age. Коллекция r3 – это все оценки студентов (пятнадцать элементов int).



3. Операторы упорядочивания.

Данные операторы выполняют сортировку коллекций. Операторы OrderBy() и OrderByDescending() выполняют сортировку по возрастанию или убыванию соответственно. Имеется версия данных операторов, принимающая в качестве дополнительного параметра объект, реализующий IComparer.

IOrderedEnumerable OrderBy(this IEnumerable source,

Func keySelector);


IOrderedEnumerable OrderByDescending(this IEnumerable src,

Func keySelector);

Интерфейс IOrderedEnumerable является наследником IEnumerable и описывает упорядоченную последовательность элементов с указанием на ключ сортировки. Если после выполнения сортировки по одному ключу требуется дополнительная сортировка по другому ключу, нужно воспользоваться операторами ThenBy() и ThenByDescending(). Имеется также оператор Reverse(), обращающий коллекцию.

Пример использования операторов упорядочивания:

var r1 = Enumerable.Reverse(gr);

var r2 = gr.OrderBy(s => s.Age);

var r3 = gr.OrderByDescending(s => s.Age).ThenBy(s => s.Name);

Чтобы получить коллекцию r1, метод расширения использовался как обычный статический метод класса, так как у List имеется собственный метод Reverse(). В коллекции r3 студенты упорядочены по убыванию возраста, а при совпадении возрастов – по фамилиям в алфавитном порядке.



4. Оператор группировки GroupBy().

Оператор группировки GroupBy() разбивает коллекцию на группы элементов с одинаковым значением некоторого ключа. Оператор GroupBy имеет перегруженные версии, позволяющие указать селектор ключа, преобразователь элементов в группе, объект, реализующий IEqualityComparer для сравнения ключей. Простейшая версия оператора группировки имеет следующий вид:

IEnumerable> GroupBy(this IEnumerable src,

Func keySelector);

Здесь последний параметр указывает на функцию, которая строит по элементу поле-ключ. Обычно эта функция просто выбирает одно из полей объекта. Интерфейс IGrouping унаследован от IEnumerable и содержит дополнительное типизированное свойство Key  ключ группировки.

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

var r1 = gr.GroupBy(s => s.Age);

foreach (IGrouping group in r1)

{

Console.WriteLine(group.Key);



foreach (Student student in group)

Console.WriteLine(student.Name);

}

5. Операторы соединения.

Операторы соединения применяются, когда требуется соединить две коллекции, элементы которых имеют общие атрибуты. Основным оператором соединения является оператор Join().

IEnumerable Join(this IEnumerable outer,

IEnumerable inner,

Func outerKeySelector,

Func innerKeySelector,

Func resultSelector);

Как видим, оператор Join() требует задания двух коллекций (внешней и внутренней) и трех функций. Первая функция порождает ключ из элемента внешней коллекции, вторая – из элемента внутренней коллекции, а третья функция продуцирует объект коллекции-результата. При выполнении соединения Join() итерируется по внешней коллекции и ищет соответствия с элементами внутренней коллекции. При этом возможны следующие ситуации:



  • Найдено одно соответствие – в результат включается один элемент.

  • Найдено множественное соответствие – результат содержит по элементу для каждого соответствия.

  • Соответствий не найдено – элемент не входит в результат.

Рассмотрим примеры использования оператора 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 {

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, s => year - s.Age, c => c.BirthYear,

(s, c) => new {s.Name, c.IDNumber});

// r1 содержит следующие объекты:

// {Name = "Smirnov", IDNumber = "WEF442"}

// {Name = "Smirnov", IDNumber = "HHH888"}

// {Name = "Kuznetsov", IDNumber = "WEF442"}

// {Name = "Kuznetsov", IDNumber = "HHH888"}

Оператор GroupJoin() порождает набор, группируя элементы внутренней коллекции при нахождении соответствия с элементом внешней коллекции: Если же соответствие не найдено, в результат включается пустая группа.

IEnumerable GroupJoin()(this IEnumerable outer,

IEnumerable inner,

Func outerKeySelector,

Func innerKeySelector,

Func, V> resultSelector);

Ниже приведён пример использования GroupJoin().

var r3 = cit.GroupJoin(gr, c => c.BirthYear, s => year - s.Age,

(c, group) => new { c.IDNumber,

Names = group.Select(s => s.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 Zip(this IEnumerable first,

IEnumerable second,

Func resultSelector);

Дадим простейший пример использования Zip():

int[] numbers = { 1, 2, 3, 4 };

string[] words = { "one", "two", "three" };

var res = numbers.Zip(words, (f, s) => f + " " + s); // 3 элемента



6. Операторы работы с множествами.

В LINQ to Objects имеется набор операторов для работы с множествами.

IEnumerable Distinct(this IEnumerable source);
IEnumerable Union(this IEnumerable first,

IEnumerable second);


IEnumerable Intersect(this IEnumerable first,

IEnumerable second);


IEnumerable Except(this IEnumerable first,

IEnumerable second);

Оператор Distinct() удаляет из коллекции повторяющиеся элементы. Операторы Union(), Intersect() и Except() представляют объединение, пересечение и разность двух множеств.

7. Операторы агрегирования.

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

int Count(this IEnumerable source);
long LongCount(this IEnumerable source);

Следующие операторы подсчитывают сумму и среднее значение в коллекции. При этом Num должен быть числовым типом int, long, float, double, decimal. Допускаются типы с поддержкой null (например, long?).



Num Sum(this IEnumerable<Num> source);
Num Sum(this IEnumerable source, FuncNum> selector);
Num Average(this IEnumerable<Num> source);
Num Average(this IEnumerable source, FuncNum> selector);

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



Num Min/Max(this IEnumerable<Num> source);
Num Min/Max(this IEnumerable src, FuncNum> selector);
T Min/Max(this IEnumerable source);
S Min/Max(this IEnumerable src, Func selector);

Первые две версии применяются для коллекций с числовым элементом. Последние две предполагают, что элемент коллекции реализует интерфейс IComparable.

Оператор Aggregate() позволяет выполнить для коллекции собственный алгоритм агрегирования. Его простейшая форма:

T Aggregate(this IEnumerable source, Func func);

Функция func принимает два аргумента: значение-аккумулятор и текущее значение коллекции. Результат функции перезаписывается в аккумулятор. В следующем примере оператор Aggregate() применяется для обращения строки:

var text = "The quick brown fox jumps over the lazy dog";

string[] words = text.Split(' ');

var reversed = words.Aggregate((acc, next) => next + " " + acc);



8. Операторы генерирования.

Эта группа операторов позволяет создать набор данных. Первый оператор группы – оператор Range(). Он просто выдаёт указанное количество подряд идущих целых чисел, начиная с заданного значения.

IEnumerable Range(int start, int count);

Продемонстрируем использование Range() в задаче поиска простых чисел:

var primes = Enumerable.Range(2, 999).

Where(x => !Enumerable.Range(2, (int) Math.Sqrt(x)).

Any(y => x != y && x%y == 0));

foreach (var prime in primes)

{

Console.WriteLine(prime);



}

Следующий оператор генерирования – оператор Repeat(). Он создает коллекцию, в которой указанный элемент повторяется требуемое число раз. Для ссылочных типов дублируются ссылки, а не содержимое.

IEnumerable Repeat(T element, int count);

Покажем не совсем стандартное применение Repeat() для генерирования последовательности случайных чисел:

Random rnd = new Random();

var r1 = Enumerable.Repeat(0, 20).Select(i => rnd.Next(0, 40));

Последним генерирующим оператором является оператор Empty(), который порождает пустое перечисление определенного типа.

IEnumerable Empty();



9. Операторы кванторов и сравнения.

Операторы кванторов похожи на соответствующие операторы в математической логике.

bool Any(this IEnumerable source, Func predicate);
bool Any(this IEnumerable source);
bool All(this IEnumerable source, Func predicate);
bool Contains(this IEnumerable source, T value);
bool Contains(this IEnumerable source, T value,

IEqualityComparer comparer)

Оператор Any() проверяет наличие хотя бы одного элемента в коллекции, удовлетворяющего указанному предикату. Вторая версия оператора Any() просто проверяет коллекцию на непустоту. Оператор All() возвращает true, если все элементы коллекции удовлетворяют предикату. И, наконец, оператор Contains() проверяет, входит ли заданное значение в коллекцию.

Оператор SequenceEqual() сравнивает две коллекции и возвращает true, если обе коллекции имеют одинаковую длину и их соответствующие элементы равны:

bool SequenceEqual(this IEnumerable first,

IEnumerable second);


bool SequenceEqual(this IEnumerable first,

IEnumerable second,

IEqualityComparer comparer);

10. Операторы разбиения.

Операторы разбиения выделяют некую часть исходной коллекции (например, первые десять элементов).

IEnumerable Take(this IEnumerable source, int count);
IEnumerable TakeWhile(this IEnumerable source,

Func predicate);


IEnumerable Skip(this IEnumerable source, int count);
IEnumerable SkipWhile(this IEnumerable source,

Func predicate);



11. Операторы элемента.

Эта группа операторов предназначена для выделения из коллекции единственного элемента, удовлетворяющего определенным условиям.

Оператор First() выделяет первый элемент (или первый элемент, удовлетворяющий определенному предикату).

T First(this IEnumerable source);


T First(this IEnumerable source, Func predicate);

Если в коллекции нет элементов, или не нашлось элементов, удовлетворяющих предикату, оператор First() выбрасывает исключение InvalidOperationException. Если требуется, чтобы исключение не выбрасывалось, а возвращалось предопределенное значение для типа, следует использовать оператор FirstOrDefault().

T FirstOrDefault(this IEnumerable source);
T FirstOrDefault(this IEnumerable source,

Func predicate);

Аналогично работают операторы Last() и LastOrDefault() для выделения последнего элемента.

Операторы Single() и SingleOrDefault() рассчитаны на то, что коллекция (или набор элементов, удовлетворяющих предикату) будет состоять из одного элемента, который данные операторы и возвращают. Если в коллекции нет элементов, или их оказалось больше одного, оператор Single() выбрасывает исключение InvalidOperationException. Оператор SingleOrDefault() выбрасывает такое исключение, если элементов оказалось больше одного.

Пара операторов ElementAt() и ElementAtOrDefault() пытаются вернуть элемент на указанной целочисленной позиции.

Оператор DefaultIfEmpty() проверяет коллекцию на пустоту. Если в коллекции нет элементов, то возвращается или значение по умолчанию для типа, или указанное значение. Если коллекция непустая, то она и возвращается.

IEnumerable DefaultIfEmpty(this IEnumerable source);
IEnumerable DefaultIfEmpty(this IEnumerable source,

T defaultValue);



12. Операторы преобразования.

Назначение данных операторов  преобразования универсальных коллекций, реализующих IEnumerable, в конкретные типы.

Оператор AsEnumerable() возвращает свой аргумент, приведенный к типу IEnumerable. Этот оператор необходим в том случае, если тип аргумента source имеет методы, аналогичные имеющимся в LINQ to Objects.

IEnumerable AsEnumerable(this IEnumerable source);

Операторы ToArray() и ToList() выполняют преобразование коллекции в массив или список.

T[] ToArray(this IEnumerable source);


List ToList(this IEnumerable source);

Существует несколько версий оператора ToDictionary(), порождающего из коллекции словарь. Например, следующая версия использует отдельные функции для выделения из элемента коллекции ключа и значения ключа.

Dictionary ToDictionary(this IEnumerable source,

Func keySelector,

Func valueSelector);

Оператор ToLookup() преобразовывает коллекцию в объект класса Lookup из пространства имён System.Linq. Класс Lookup представляет словарь, в котором ключу сопоставлен не единственный элемент, а набор элементов.

ILookup ToLookup(this IEnumerable source,

Func keySelector,

Func valueSelector);

Оператор OfType() итерируется по коллекции и генерирует список, содержащий только элементы заданного типа T.

IEnumerable OfType(this IEnumerable source);

Оператор Cast() итерируется по слаботипизированной коллекции и пытается выполнить приведение каждого элемента к указанному типу. Если приведение выполнить не удаётся, генерируется исключение. Данный оператор полезен, если мы хотим применять LINQ для старых коллекций, таких, как ArrayList.

IEnumerable Cast(this IEnumerable source);

Из операторов преобразования отложенные вычисления выполняют операторы AsEnumerable(), OfType(), Cast().

Язык C# версии 3.0 вводит новые ключевые слова и особые синтаксические расширения для записи некоторых операторов LINQ в виде выражений запросов. Составленное выражение запросов должно подчиняться следующим правилам (за строгим описанием правил следует обратиться к MSDN):

1. Выражение должно начинаться с конструкции from, которая указывает на обрабатываемую коллекцию.

2. Затем выражение может содержать ноль или более конструкции from, let или where. Конструкция let представляет переменную и присваивает ей значение. Конструкция where фильтрует элементы коллекции.

3. Затем выражение может включать ноль или более конструкций orderby, с полями сортировки и необязательным указанием на направление упорядочивания. Направление может быть ascending или descending.

4. Затем следует конструкция select или group.

5. Наконец, в оставшейся части выражения может следовать необязательная конструкция продолжения. Такой конструкцией является into.

Выражения запросов транслируются компилятором в вызовы соответствующих операторов LINQ. Приведем некоторые примеры эквивалентной записи запросов к коллекциям данных.

var r1 = gr.Where(s => s.Age > 20).Select(s => s.Marks);

// эквивалентный синтаксис

var r2 = from s in gr

where s.Age > 20

select s.Marks;


var r3 = gr.OrderByDescending(s => s.Age).ThenBy(s => s.Name);

// эквивалентный синтаксис

var r4 = from s in gr

orderby s.Age descending, s.Name

select s;
var r5 = gr.Select(s => new { s.Name, s.Age });

// эквивалентный синтаксис

var r6 = from s in gr select new { s.Name, s.Age };


Каталог: images
images -> В списке студентов (или магистрантов)
images -> Н. И. Сулейманов Комплект контрольно-оценочных средств для оценки результатов освоения профессионального модуля разработан на основе Федерального государственного образовательного стандарта среднего профессионального
images -> По направлению подготовки
images -> Добавить гаджеты. Добавление гаджетов
images -> Техническое задание № apnip/C. 2/CS/Ind/01 Международный консультант по улучшенной производительности орошаемого земледелия
images -> Комплект контрольно-оценочных средств по профессиональному модулю пм. 01 Техническое обслуживание и ремонт автотранспорта


Поделитесь с Вашими друзьями:
1   ...   6   7   8   9   10   11   12   13   ...   29




База данных защищена авторским правом ©vossta.ru 2022
обратиться к администрации

    Главная страница