Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект лекций (C#).pdf
Скачиваний:
39
Добавлен:
25.03.2016
Размер:
2.43 Mб
Скачать

Peoples[5] = new People() { Surname = "Попов", YearOfBirth = 1980, City = "Новокузнецк" };

var Result = from p in Peoples

orderby p.City, p.YearOfBirth, p.Surname

group p by new { city = p.City, year = p.YearOfBirth }; Result_TB.Clear();

foreach (var group in Result)

{

Result_TB.AppendText(group.Key.city + ", " + group.Key.year + "\n");

foreach (var p in group)

 

Result_TB.AppendText("

" + p.Surname + "\n");

}

 

Результат будет выведен в виде:

Москва, 1980 Степанов Москва, 1999

Петров Новокузнецк, 1980

Иванов

Попов Новокузнецк, 2001

Антонов Псков, 1971

Иванов

В данном примере в операторе

group p by new { city = p.City, year = p.YearOfBirth }

создается анонимный тип с полями city и year, заполнение которых осуществляется из p.City и p.YearOfBirth соответственно.

При выводе значения ключа

Result_TB.AppendText(group.Key.city + ", " + group.Key.year + "\n");

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

9.2.1 into : обработка результатов группировки

Результаты запроса, использующего группировку, могут также подвергаться обработке, как и данные из <источника данных>.

Для этого оператор группировки записывается в формате

group <возвращаемые данные> by <ключ группировки> into <переменная группы>

154

Result_TB

где <переменная группы> – переменная, с которой далее могут выполняться различные действия с использованием операторов where, orderby, select. Фактически создание <переменной группы> начинает новый запрос по обработке данных группы, который также должен заканчиваться операторами select или droup.

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

TextBox.

People[] Peoples = new People[6];

Peoples[0] = new People() { Surname = "Иванов", YearOfBirth = 1980, City = "Новокузнецк" };

Peoples[1] = new People() { Surname = "Петров", YearOfBirth = 1999, City = "Москва" };

Peoples[2] = new People() { Surname = "Иванов", YearOfBirth = 1971, City = "Псков" };

Peoples[3] = new People() { Surname = "Степанов", YearOfBirth = 1980, City = "Москва" };

Peoples[4] = new People() { Surname = "Антонов", YearOfBirth = 2001, City = "Новокузнецк" };

Peoples[5] = new People() { Surname = "Попов", YearOfBirth = 1980, City = "Новокузнецк" };

var Result = from p in Peoples orderby p.Surname

group p by p.City into g where g.Count() > 1 orderby g.Key

select g; Result_TB.Clear();

foreach (var group in Result)

{

Result_TB.AppendText(group.Key + "\n"); foreach (var p in group)

Result_TB.AppendText(" "+p.Surname + "\n");

}

Результат будет выведен в виде:

Москва

Петров

Степанов

Новокузнецк

Антонов

Иванов

Попов

155

9.2.1 let : временные переменные в запросе

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

let <переменной> = <значение>

Пример: сгруппировать данные о людях по длине их фамилии и вывести в порядке возрастания длин. В пределах каждой группы выводить людей в алфавитном порядке фамилий. Вывод осуществлять в компонент Result_TB класса TextBox.

People[] Peoples = new People[6];

Peoples[0] = new People() { Surname = "Иванов", YearOfBirth = 1980, City = "Новокузнецк" };

Peoples[1] = new People() { Surname = "Петров", YearOfBirth = 1999, City = "Москва" };

Peoples[2] = new People() { Surname = "Иванов", YearOfBirth = 1971, City = "Псков" };

Peoples[3] = new People() { Surname = "Степанов", YearOfBirth = 1980, City = "Москва" };

Peoples[4] = new People() { Surname = "Антонов", YearOfBirth = 2001, City = "Новокузнецк" };

Peoples[5] = new People() { Surname = "Попов", YearOfBirth = 1980, City = "Новокузнецк" };

var Result = from p in Peoples

let b = p.Surname.Length orderby p.Surname

group p by b into g orderby g.Key select g;

Result_TB.Clear();

foreach (var group in Result)

{

Result_TB.AppendText(group.Key + "\n"); foreach (var p in group)

Result_TB.AppendText(" "+p.Surname + "\n");

}

Результат будет выведен в виде:

5

Попов

6

Иванов

Иванов

Петров

7

Антонов

156

8

Степанов

9.2.2 from : использование нескольких источников данных

Оператор from в LINQ во многом аналогичен оператору цикла foreach, т.к. он организует перебор элементов в <источнике запросов> для их последующей обработки. Поэтому последовательное расположение нескольких операторов from фактически реализует вложенные циклы, обеспечивающие формирование всех возможных комбинаций элементов (декартово произведение).

Пример: построение таблицы перемножения всех возможных пар элементов двух одномерных целочисленных массивов.

int[] Mas1 = { 1, 2 };

int[] Mas2 = { 1, 2, 3, 4 }; var Result = (from c1 in Mas1 from c2 in Mas2

select new { C1 = c1, C2 = c2, Pr = c1 * c2 }).ToArray(); // Result = {[1, 1, 1], [1, 2, 2], [1, 3, 3], [1, 4, 4],

[2, 1, 2], [2, 2, 4], [2, 3, 6], [2, 4, 8]}

В большинстве случаев не требуется получать полное декартово произведение. Поэтому в запросе используются условия отбора.

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

int[] Mas1 = { 1, 2 };

int[] Mas2 = { 1, 2, 3, 4 }; var Result = (from c1 in Mas1 from c2 in Mas2 where c1 != c2

select new { C1 = c1, C2 = c2, Pr = c1 * c2 }).ToArray(); // Result = {[1, 2, 2], [1, 3, 3], [1, 4, 4],

[2, 1, 2], [2, 3, 6], [2, 4, 8]}

9.2.3 join : соединение данных из нескольких источников

Если в предыдущем примере оператор != заменить на оператор ==, то будет произведен отбор пар элементов, имеющих одинаковое значение.

Задача получения пар элементов, имеющих одинаковые значения некоторых данных в разных источниках данных, получила название «соединение», и для нее в LINQ имеется специальный оператор.

Простейшая схема соединения для двух источников данных (внутреннее со-

единение) имеет вид:

157

from <переменная диапазона 1> in <источник данных 1> join <переменная диапазона 2> in <источник данных 2> on <переменная диапазона 1>.<свойство> equals

<переменная диапазона 2>.<свойство>

При выполнении данного оператора будут из двух источников отбираться

только

те

пары

элементов,

у

которых

значение

<переменная диапазона 1>.<свойство>

совпадает

со

значением

<переменная диапазона 2>.<свойство>.

Для рассмотрения приемов работы по соединению данных будем использовать два класса: Student и Group.

class Group

 

{

 

public int ID;

// Уникальный идентификатор группы

public string Name;

// Наименование группы

}

 

class Student

 

{

 

public string Surname;

// Фамилия студента

public int YearOfBirth;

// Год рождения студента

public int GroupID;

// Принадлежность к группе

}

 

Пример: вывести студентов в алфавитном порядке фамилий с указанием в скобках группы обучения. Вывод осуществлять в компонент Result_TB класса

TextBox.

Group[] Groups = new Group[4];

Groups[0] = new Group() { ID = 1, Name = "АБВ" };

Groups[1] = new Group() { ID = 2, Name = "ННН" };

Groups[2] = new Group() { ID = 3, Name = "ЯАП" };

Groups[3] = new Group() { ID = 4, Name = "КЕП" };

Student[] Students = new Student[6];

Students[0] = new Student() { Surname = "Иванов",

YearOfBirth = 1990, GroupID = 1 };

Students[1] = new Student() { Surname = "Петров",

YearOfBirth = 1985, GroupID = 4 };

Students[2] = new Student() { Surname = "Смирнов",

YearOfBirth = 1990, GroupID = 3 }; Students[3] = new Student() { Surname = "Антонов",

YearOfBirth = 1993, GroupID = 1 };

Students[4] = new Student() { Surname = "Иванов",

YearOfBirth = 1993, GroupID = 4 }; Students[5] = new Student() { Surname = "Петров",

YearOfBirth = 1992, GroupID = 4 };

var Result = from g in Groups

join s in Students on g.ID equals s.GroupID orderby s.Surname

select new { gName = g.Name, sName = s.Surname };

158

Result_TB.Clear(); foreach (var p in Result)

Result_TB.AppendText(p.sName + " (" + p.gName + ")\n");

Результат будет выведен в виде:

Антонов (АБВ) Иванов (АБВ) Иванов (КЕП) Петров (КЕП) Петров (КЕП) Смирнов (ЯАП)

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

Для указания необходимости группировки условие группировки дополняется оператором into в соответствии со следующей схемой:

on <переменная диапазона 1>.<свойство> equals

<переменная диапазона 2>.<свойство> into <переменная группы>

<переменная группы> представляет собой интерфейс IEnumerable<T>, где

<T> соответствует типу <переменной диапазона 2>.

Пример: вывести студентов, сгруппировав их по группам. Группы выводить в алфавитном порядке названия. Вывод осуществлять в компонент Result_TB класса

TextBox.

Group[] Groups = new Group[4];

Groups[0] = new Group() { ID = 1, Name = "АБВ" };

Groups[1] = new Group() { ID = 2, Name = "ННН" };

Groups[2] = new Group() { ID = 3, Name = "ЯАП" };

Groups[3] = new Group() { ID = 4, Name = "КЕП" };

Student[] Students = new Student[6];

Students[0] = new Student() { Surname = "Иванов",

YearOfBirth = 1990, GroupID = 1 };

Students[1] = new Student() { Surname = "Петров",

YearOfBirth = 1985, GroupID = 4 };

Students[2] = new Student() { Surname = "Смирнов",

YearOfBirth = 1990, GroupID = 3 };

Students[3] = new Student() { Surname = "Антонов",

YearOfBirth = 1993, GroupID = 1 };

Students[4] = new Student() { Surname = "Иванов",

YearOfBirth = 1993, GroupID = 4 };

Students[5] = new Student() { Surname = "Петров",

YearOfBirth = 1992, GroupID = 4 };

159

var Result = from g in Groups

join s in Students on g.ID equals s.GroupID into r orderby g.Name

select new

{

gName = g.Name, students = r

}; Result_TB.Clear();

foreach (var group in Result)

{

Result_TB.AppendText(group.gName + "\n"); foreach (var p in group.students)

Result_TB.AppendText(" " + p.Surname + "\n");

}

Результат будет выведен в виде:

АБВ

Иванов

Антонов

КЕП

Петров

Иванов

Петров

ННН

ЯАП

Смирнов

Для того, чтобы вывести студентов в пределах группы в алфавитном порядке фамилий, требуется отсортировать переменную группы r. Это можно сделать с помощью вложенного запроса:

var Result = from g in Groups

join s in Students on g.ID equals s.GroupID into r orderby g.Name

select new

{

gName = g.Name, students = from p in r

orderby p.Surname select p

}; Result_TB.Clear();

foreach (var group in Result)

{

Result_TB.AppendText(group.gName + "\n"); foreach (var p in group.students)

Result_TB.AppendText(" " + p.Surname + "\n");

}

Результат будет выведен в виде:

160

АБВ

Антонов

Иванов

КЕП

Иванов

Петров

Петров

ННН

ЯАП

Смирнов

Как видно из примера, в группе «ННН» нет студентов.

Если при такой ситуации требуется вывести в элемент с нужными значениями по умолчанию, то используется левое внешнее соединение. Для реализации такого соединения переменной группы задаются требуемые значения по умолчанию с помощью метода DefaultIfEmpty(): (для примера, значение имени студента будет задано «Нет студентов»)

var Result = from g in Groups

join s in Students on g.ID equals s.GroupID into r orderby g.Name

select new

{

gName = g.Name,

students = from p in r.DefaultIfEmpty( new Student()

{GroupID = 0,

Surname = "Нет студентов",

YearOfBirth = 0

})

orderby p.Surname select p

}; Result_TB.Clear();

foreach (var group in Result)

{

Result_TB.AppendText(group.gName + "\n"); foreach (var p in group.students)

Result_TB.AppendText(" " + p.Surname + "\n");

}

Результат будет выведен в виде:

АБВ

Антонов

Иванов

КЕП

Иванов

Петров

Петров

ННН

Нет студентов

161