- •1 Основные сведения о C#
- •1.1 Особенности языка
- •1.2 Типы данных
- •1.3 Переменные
- •1.4 Константы (литералы)
- •1.5 Операторы, используемые при построении выражений
- •1.6 Класс Object
- •1.7 Класс Math
- •1.8 Класс Convert
- •1.9 Пространство имён
- •1.10 Типы, допускающие значение null
- •2 Операторы и конструкции С#
- •2.1 Операторы присваивания
- •2.2 Приведение типов
- •2.3 Операторы инкремента и декремента
- •2.4 Операторные скобки {}
- •2.5 Условный оператор if
- •2.6 Логические операторы «И» и «ИЛИ»
- •2.7 Условный оператор ? :
- •2.8 Оператор выбора switch и оператор прерывания break
- •2.9 Оператор цикла for
- •2.10 Оператор цикла while
- •2.11 Оператор цикла do...while
- •2.12 Операторы прерываний break (для циклов) и continue
- •2.13 Оператор new
- •2.14 Массивы
- •2.14.1 Одномерные массивы
- •2.14.2 Многомерные массивы
- •2.14.3 Ступенчатые массивы
- •2.14.4 Работа с массивами как с объектами
- •2.15 Оператор цикла foreach
- •2.16 Строки
- •2.17 Перечисления
- •2.18 Обработка исключений
- •2.18.1 Класс Exception и стандартные исключения
- •2.18.2 Блок try...catch
- •2.18.3 Блок try...finally
- •2.18.4 Блок try...catch...finally
- •2.18.5 Оператор throw
- •3 Классы. Основные понятия
- •3.1 Общая схема
- •3.2 Спецификаторы доступа
- •3.3 Поля
- •3.4 Создание объекта и доступ к его членам
- •3.5 Методы
- •3.5.1 Перегрузка методов
- •3.5.2 Новое в версии C# 4.0
- •3.6 Конструкторы
- •3.7 Деструкторы
- •3.8 Инициализаторы объектов
- •3.9 Свойства
- •3.10 Индексаторы
- •4 Классы. Расширенное использование
- •4.1 Статические классы и члены классов
- •4.2 Наследование
- •4.2.1 Наследование и конструкторы
- •4.2.2 Переопределение членов класса
- •4.3 Полиморфизм
- •4.3.1 Виртуальные методы
- •4.3.2 Абстрактные классы и члены классов
- •4.3.3 Операторы as и is
- •4.3.4 Модификатор sealed
- •4.4 Перегрузка операторов
- •5 Интерфейсы
- •6 Делегаты, лямбда-выражения и события
- •6.1 Делегаты
- •6.2 Анонимные методы и лямбда-выражения
- •6.3 События
- •7 Универсальные типы
- •7.1 Общая схема
- •7.2 Ограничения по параметрам типа
- •7.2.1 Ограничение на базовый класс
- •7.2.2 Ограничение на интерфейс
- •7.2.3 Ограничение на конструктор
- •7.2.4 Ограничения ссылочного типа и типа значения
- •7.3 Параметры типы в методах
- •7.4 Некоторые универсальные типы С#
- •7.4.1 Класс Array
- •7.4.2 Класс List<T>
- •7.4.3 Класс LinkedList<T>
- •7.4.4 Класс Queue<T>
- •7.4.5 Класс Stack<T>
- •7.4.6 Классы SortedSet<T> и HashSet<T>
- •7.4.7 Классы Dictionary<TKey, TValue> и SortedDictionary<TKey, TValue>
- •8 Работа с файлами
- •8.1 Класс File
- •8.2 Работа с файлами как с потоками
- •8.2.1 Класс FileStream
- •8.2.2 Класс StreamReader
- •8.2.3 Класс StreamWriter
- •8.2.4 Класс BinaryReader
- •8.2.5 Класс BinaryWriter
- •9 LINQ
- •9.1 Программные конструкции и типы, используемые LINQ
- •9.1.1 Методы расширения
- •9.1.2 Анонимные типы
- •9.1.3 Интерфейс IEnumerable<T>
- •9.2 Построение запросов на LINQ
- •9.2.1 Общая структура запроса
- •9.2.2 Простой запрос
- •9.2.3 where : использование условий отбора
- •9.2.4 orderby : использование сортировки
- •9.2.5 select : определение возвращаемого значения
- •9.2.1 group : группировка данных
- •9.2.1 into : обработка результатов группировки
- •9.2.1 let : временные переменные в запросе
- •9.2.2 from : использование нескольких источников данных
- •9.2.3 join : соединение данных из нескольких источников
- •9.3 Получение результатов с использованием методов
- •9.3.1 Метод Where
- •9.3.2 Метод Select
- •9.3.3 Методы сортировки
- •9.3.4 Метод GroupBy
- •9.3.5 Метод Join
- •9.3.6 Дополнительные методы интерфейса IEnumerable<T>
- •9.4 Совместное использование запросов и методов
where <условие 1> [where <условие 2> ...]
Например, если в предыдущем примере требуется отобрать людей, год рождения которых больше 1980 и меньше 2000, то условие отбора, реализованное по первой схеме, можно записать в виде:
where p.YearOfBirth > 1980 && p.YearOfBirth < 2000
а по второй схеме в виде:
where p.YearOfBirth > 1980 where p.YearOfBirth < 2000
При применении второй схемы несколько блоков where объединяются логическим оператором «И». Поэтому вторая схема имеет меньшие возможности по применению.
В условиях отбора могут использоваться любые выражения C#, дающие логический результат.
Пример: отобрать людей, фамилия которых начинается на букву «И», а год рождения нечетный.
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 = "Новокузнецк" };
People[] Result = (from p in Peoples
where p.Surname.StartsWith("И") && p.YearOfBirth % 2 == 1 select p).ToArray();
// Result = {[Иванов, 1971, Псков]}
9.2.4 orderby : использование сортировки
Сортировка позволяет расположить получаемые данные в требуемом порядке. Как правило, сортировка осуществляется по возрастанию или по убыванию значений данных, однако нет никаких ограничений на реализацию сортировки данных любым требуемым способом.
Для обеспечения сортировки в запрос вводится конструкция вида:
orderby <правило сортировки> [ascending | descending]
148
Как правило, указание признака сортировки размещается после определения условий отбора (см. общую схему в разделе 9.2.1).
Пример: отобрать из массива людей, которые родились позднее 1980 года, и вывести их в алфавитном порядке фамилий.
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 = "Новокузнецк" };
People[] Result = (from p in Peoples
where p.YearOfBirth > 1980 orderby p.Surname
select p).ToArray();
//Result = {[Антонов, 2001, Новокузнецк], [Петров, 1999, Москва]}
Вкачестве правила сортировки может выступать не только <переменная диапазона>, но и различные выражения.
Пример: отобрать людей, фамилия которых не начинается на букву «И», и вывести их в порядке убывания столетия рождения.
People[] Result = (from p in Peoples
where !p.Surname.StartsWith("И") orderby p.YearOfBirth / 100 descending select p).ToArray();
// Result = {[Антонов, 2001, Новокузнецк], [Петров, 1999, Москва], [Степанов, 1980, Москва]}
Следует отметить, что если есть данные, имеющие одно и то же значение с точки зрения <правило сортировки>, то они выводятся в порядке их расположения в <источнике данных> (например, данные о втором и третьем человеке в предыдущем примере).
Сортировка результатов запроса может осуществляться по нескольким правилам. В этом случае пары «правило – направление» перечисляются через запятую.
Алгоритм сортировки по нескольким правилам следующий. Изначально все данные сортируются по первому правилу. Если некоторая группа данных имеет одинаковое значение с точки зрения первого правила сортировки, то эта группа сортируется по второму правилу, и т.д.
Пример: отобрать людей, родившихся в 20 веке, и вывести их в порядке убывания года рождения. При равенстве у нескольких людей года рождения, выводить их в алфавитном порядке фамилий.
149
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 = "Новокузнецк" };
People[] Result = (from p in Peoples
where p.YearOfBirth > 1900 && p.YearOfBirth <= 2000 orderby p.YearOfBirth descending, p.Surname
select p).ToArray();
// Result = {[Петров, 1999, Москва], [Иванов, 1980, Новокузнецк], [Попов, 1980, Новокузнецк], [Степанов, 1980, Москва], [Иванов, 1971, Псков]}
9.2.5 select : определение возвращаемого значения
Во всех предыдущих примерах результатами запроса являлась переменная диапазона. Однако результатом запроса может быть любой требуемый набор данных: часть переменной диапазона, результаты выражений, вновь созданный объект и т.п.
Пример 1: получить в виде одномерного массива года рождения людей.
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 = "Новокузнецк" };
int[] Result = (from p in Peoples
select p.YearOfBirth).ToArray(); // Result = {1980, 1999, 1971, 1980, 2001, 1980}
150
Пример 2: получить в виде одномерного массива возрасты людей на начало 21 века (считать год рождения за целый год), отсортированные в порядке возрастания.
int[] Result = (from p in Peoples
where p.YearOfBirth <= 2000 orderby 2001 - p.YearOfBirth
select 2001 - p.YearOfBirth).ToArray(); // Result = {2, 21, 21, 21, 30}
Пример 3: создать класс, включающий фамилию человека и возраст на начало 21 века (считать год рождения за целый год). Сформировать массив элементов данного класса на основе исходного массива.
class ShortPeople
{
public string Surname; public int Age2001;
}
ShortPeople[] Result = (from p in Peoples where p.YearOfBirth <= 2000
select new ShortPeople() { Surname = p.Surname, Age2001 = 2001 - p.YearOfBirth }).ToArray();
// Result = {[Иванов, 21], [Петров, 2], [Иванов, 30], [Степанов, 21], [Попов, 21]}
Пример 4: реализовать пример 3 без создания нового класса.
var Result = (from p in Peoples
where p.YearOfBirth <= 2000 select new { Surname = p.Surname,
Age2001 = 2001 - p.YearOfBirth }).ToArray(); // Result = {[Иванов, 21], [Петров, 2], [Иванов, 30],
[Степанов, 21], [Попов, 21]}
В примере 4 создается анонимный тип, имеющий поле Surname строкового типа и целочисленное поле Age2001. Выполнение отбора может быть реализовано, например, таким образом:
foreach (var p in Result)
{
// p.Surname, p.Age2001 – доступ к полям объекта
}
9.2.1 group : группировка данных
Группировка позволяет сгруппировать данные в результате запроса по некоторому признаку (ключу). Поэтому типом элемента результата запроса является интерфейс IGrouping<TKey, TElement>, хранящий ключ типа TKey (доступ к ко-
151
торому осуществляется через свойство Key) и коллекцию элементов, относящихся к данному ключу (в виде интерфейса IEnumerable<TElement>).
Формальная схема оператора группировки имеет вид:
group <возвращаемые данные> by <ключ группировки>
Оператор group применяется вместо оператора select.
<возвращаемые данные> формируются по тем же правилам, что и в опера-
торе select.
<ключ группировки>, как правило, связан с <переменной диапазона>.
Однако <ключом группировки> может быть любое выражение, в том числе не связанное с <переменной диапазона>.
Пример: сгруппировать данные о людях по городу их проживания и вывести в алфавитном порядке названий городов. В пределах каждого города выводить людей в алфавитном порядке фамилий. Вывод осуществлять в компонент
класса TextBox.
People[] Peoples = new People[6];
Peoples[0] = new People() { Surname = "Иванов", YearOfBirth = 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.City, p.Surname group p by p.City;
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");
}
Результат будет выведен в виде:
Москва
Петров
Степанов
Новокузнецк
Антонов
Иванов
152
Попов
Псков
Иванов
Рассмотрим более подробно формирование запроса, его выполнение и вывод результата. При формировании запроса с помощью оператора
orderby p.City, p.Surname
осуществляется сортировка всех данных, чтобы при формировании списка групп и списка людей в каждой из групп они выводились в алфавитном порядке. При этом первым правилом сортировки является упорядочивание по названию города, ведь именно оно образует первый уровень иерархии результата запроса.
Далее в операторе
group p by p.City
производится собственно отбор людей с группировкой по ключу, которым является название города проживания.
Так как результат запроса имеет двухуровневую структуру (город-люди), то для вывода результата требуется использование двух вложенных циклов. Первый цикл
foreach (var group in Result)
перебирает все группы, имеющиеся в результате запроса (и выводит название города с помощью group.Key). Второй цикл
foreach (var p in group)
перебирает всех людей в группе и выводит их фамилии.
Группировка может производиться и по нескольким ключам. В этом случае используется составной ключ, создаваемый с помощью анонимного типа или именованного типа для хранения элементов ключа.
Пример: сгруппировать данные о людях по городу их проживания и году рождения и вывести в алфавитном порядке названий городов и увеличения года рождения. В пределах каждой группы выводить людей в алфавитном порядке фамилий. Вывод осуществлять в компонент 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 = "Новокузнецк" };
153