- •Учебное пособие по дисциплине «программирование»
- •Оглавление
- •1 С# 6.0 и платформа .Net 4.6 6
- •С# 6.0 и платформа .Net 4.6
- •Платформа Microsoft .Net Framework.
- •Понятия приложения, проекта, решения
- •Среда разработки Visual Studio .Net
- •Создание первого проекта
- •Компиляция и выполнение программы в среде clr
- •Технология объектно-ориентированного программирования
- •Состав языка
- •Типы данных
- •Переменные и константы
- •Организация ввода-вывода данных. Форматирование.
- •Вывод данных
- •Ввод данных
- •Операции
- •3. Отрицание:
- •9. Условная операция.
- •Выражения и преобразование типов
- •Операторы языка c#
- •Операторы следования
- •Операторы ветвления
- •Оператор выбора switch
- •Операторы цикла
- •Операторы безусловного перехода
- •Методы: основные понятия
- •Необязательные параметры
- •Именованные параметры
- •Перегрузка методов
- •Рекурсивные методы
- •Обработка исключений
- •Оператор try
- •Операторы checked и unchecked
- •Генерация собственных исключений
- •Полезные совет
- •Массивы
- •Одномерные массивы
- •Многомерные массивы
- •Ступенчатые массивы
- •Оператор foreach и его использование при работе с массивами
- •Символы и строки
- •Символы char
- •Неизменяемые строки string
- •Изменяемые строки
- •Регулярные выражения
- •Метасимволы в регулярных выражениях
- •Поиск в тексте по шаблону
- •Редактирование текста
- •Организация с#-системы ввода-вывода
- •Байтовый поток
- •Символьный поток
- •Двоичные потоки
- •Перенаправление стандартных потоков
- •Работа с файловой системой
- •Работа с каталогами Абстрактный класс FileSystemInfo
- •Класс DirectoryInfo
- •Класс Directory
- •Работа с файлами Класс Filelnfo
- •Класс File
- •Основные понятия
- •Данные: поля и константы
- •Конструкторы
- •Конструкторы экземпляра
- •Конструкторы класса
- •Свойства
- •«Один класс – один файл»,
- •Классы (продолжение)
- •Деструкторы
- •Индексаторы
- •Операции класса
- •Унарные операции
- •Бинарные операции
- •Операции преобразования типов
- •Иерархия классов
- •Наследование
- •Использование защищенного доступа
- •Наследование конструкторов
- •1) Позволяет вызвать конструктор базового класса:
- •2) Позволяет получить доступ к члену базового класса, который скрыт "за" членом производного класса.
- •Многоуровневая иерархия
- •Переменные базового класса и производного класса
- •Виртуальные методы
- •Абстрактные методы и классы
- •Запрет наследования
- •Обобщенные типы
- •Значения по умолчанию
- •Статические поля обобщенных классов
- •Ограничения универсальных типов
- •Использование нескольких универсальных параметров
- •Обобщенные методы
- •Наследование обобщенных типов
- •Анонимные типы
- •Методы расширения
- •Интерфейсы
- •Введение в интерфейсы
- •Интерфейсы в преобразованиях типов
- •Обобщенные интерфейсы
- •Явное применение интерфейсов
- •Клонирование объектов. Интерфейс iCloneable
- •Сортировка объектов. Интерфейс iComparable
- •Применение компаратора
- •Ковариантность и контравариантность обобщенных интерфейсов
- •Структуры
- •Перечисления enum
- •Коллекции
- •Введение в коллекции
- •Необобщенные коллекции
- •Обобщенные коллекции
- •Инициализация словарей
- •Класс ObservableCollection
- •Индексаторы и создание коллекций
- •Интерфейсы iEnumerable и iEnumerator
- •Итераторы и оператор yield
- •Именованный итератор
- •Класс Hashtable
- •Делегаты, события и лямбды
- •Делегаты
- •События
- •Класс данных события AccountEventArgs
- •Анонимные методы
- •Ковариантность и контравариантность делегатов
- •Ковариантность и контравариантность в обобщенных делегатах
- •Делегаты Action, Predicate и Func
- •Встроенные Методы (Expression–Bodied)
- •Сериализация
- •Введение в сериализацию объектов
- •Атрибут Serializable
- •Формат сериализации
- •Бинарная сериализация. BinaryFormatter
- •Сериализация в формат soap. SoapFormatter
- •Сериализация в xml. XmlSerializer
- •Сериализация в json. DataContractJsonSerializer
- •Сборка мусора, управление памятью и указатели
- •Сборщик мусора в c#
- •Класс System.Gc
- •Финализируемые объекты
- •Создание деструкторов
- •Интерфейс iDisposable
- •Комбинирование подходов
- •Общие рекомендации по использованию Finalize и Dispose
- •Указатели
- •Ключевое слово unsafe
- •Операции * и &
- •Получение адреса
- •Операции с указателями
- •Указатель на другой указатель
- •Указатели на структуры, члены классов и массивы
- •Указатели на массивы и stackaloc
- •Оператор fixed и закрепление указателей
- •Dlr в c#. Ключевое слово dynamic
- •Сборки .Net
- •Роль сборок в приложениях .Net
- •Манифест сборки
- •Атрибуты сборки
- •Разделяемые сборки. Добавление сборки в gac
- •Строгое имя сборки
- •Многопоточность
- •Введение в многопоточность. Класс Thread
- •Получение информации о потоке
- •Статус потока
- •Приоритеты потоков
- •Создание потоков. Делегат ThreadStart
- •Потоки с параметрами и ParameterizedThreadStart
- •Синхронизация потоков
- •Мониторы
- •Класс AutoResetEvent
- •Мьютексы
- •Семафоры
- •Использование таймеров
- •Параллельное программирование и библиотека tpl
- •Задачи и класс Task
- •Массив задач
- •Работа с классом Task
- •Свойства класса Task
- •Возвращение результатов из задач
- •Задачи продолжения
- •Класс Parallel
- •Выход из цикла
- •Отмена задач и параллельных операций. CancellationToken
- •Отмена параллельных операций Parallel
- •Aсинхронное программирование
- •Асинхронные делегаты
- •IAsyncResult и методы BeginInvoke/EndInvoke
- •Асинхронные методы, async и await
- •Последовательный и параллельный вызов асинхронных методов
- •Обработка ошибок в асинхронных методах
- •Обработка исключений в async void-методах
- •Обработка нескольких исключений. WhenAll
- •Await в блоках catch и finally
- •Отмена асинхронных операций
- •Рефлексия
- •Введение в рефлексию. Класс System.Type
- •Получение типа
- •Применение рефлексии и исследование типов
- •Получение информации о методах
- •Получение конструкторов
- •Получение информации о полях и свойствах
- •Поиск реализованных интерфейсов
- •Динамическая загрузка сборок и позднее связывание
- •Позднее связывание
- •Атрибуты в .Net
- •Ограничение применения атрибута
- •Основы linq
- •Методы расширения linq
- •Список используемых методов расширения linq
- •Фильтрация выборки и проекция
- •Фильтрация
- •Выборка сложных объектов
- •Сложные фильтры
- •Проекция
- •Переменые в запросах и оператор let
- •Выборка из нескольких источников
- •Сортировка
- •Множественные критерии сортировки
- •Работа с множествами
- •Разность множеств
- •Пересечение множеств
- •Объединение множеств
- •Удаление дубликатов
- •Агрегатные операции
- •Метод Aggregate
- •Получение размера выборки. Метод Count
- •Получение суммы
- •Максимальное, минимальное и среднее значения
- •Методы Skip и Take
- •Группировка
- •Соединение коллекций. Метод Join, GroupJoin и Zip
- •Метод Zip
- •Методы All и Any
- •Отложенное и немедленное выполнение linq
- •Делегаты и анонимные методы в запросах linq
- •Работа с xml в c#
- •Работа с xml с помощью классов System.Xml
- •Изменение xml-документа
- •Удаление узлов
- •Linq to Xml. Создание Xml-документа
- •Выборка элементов в linq to xml
- •Изменение документа в linq to xml
- •Введение в Parallel linq. Метод AsParallel
- •Метод AsParallel
- •Метод ForAll
- •Метод AsOrdered
- •Обработка ошибок и отмена операции
- •Прерывание параллельной операции
- •Службы Windows
- •Создание службы для Windows
- •Установка службы
- •Сетевое программирование в с# и .Net
- •Основы работы с сетями в c# и .Net
- •Введение в сети и протоколы
- •Адреса в .Net
- •Отправка запросов
- •Класс WebClient
- •Классы WebRequest и WebResponse
- •Отправка данных в запросе
- •Обработка ошибок при запросах
- •Класс Socket
- •Общий принцип работы сокетов
- •Клиент-серверное приложение на сокетах tcp
- •Протокол tcp
- •Многопоточное клиент-серверное приложение tcp
- •Консольный tcp-чат
- •NetworkStream и текстовые потоки
- •Потоки бинарных данных
- •Протокол http
- •Руководство по Universal Windows Platform
- •Руководство по ado.Net и работе с базами данных
- •Глава 3. Работа с SqlDataAdapter и DataSet
- •Глава 4. Linq to sql
- •Введение в Entity Framework
- •Что такое Entity Framework
- •Способы взаимодействия с бд
- •Первое приложение с Entity Framework. Подход Code First
- •Взаимодействие с данными. Подходы
- •Code First к существующей базе данных
- •Соглашения по наименованию в Code First
- •Сопоставление типов
- •Названия таблиц и столбцов
- •Автоматизация Code First
- •Автоматизация Code First и ef Power Tools
- •Основы Entity Framework
- •Основные операции с данными
- •Добавление
- •Редактирование
- •Удаление
- •Строка подключения
- •Строка подключения в Model First и Database First
- •Навигационные свойства и lazy loading
- •Способы получения связанных данных
- •Связь один-к-одному
- •Связь один ко многим
- •Связь один ко многим. Практический пример
- •Связь многие ко многим
- •Инициализация базы данных
- •Параллелизм в Entity Framework
- •Управление транзакциями
- •Руководство по программированию для Xamarin Forms
- •Руководство по asp.Net Core
- •Паттерны проектирования в c# и .Net
- •Руководство по игростроению на платформе MonoGame
- •Задания для самостоятельного выполнения Рекомендации по выполнению практикума
- •Лабораторная работа. Алгебра логики и логические задачи.
- •Лабораторная работа. Алгоритмы. Алгоритмизация.
- •Цель и порядок работы
- •Контрольные вопросы
- •Задания
- •Лабораторная работа. Основы c# (.Net)
- •Работа с консолью и класс Console
- •Лабораторная работа. Основные операции с#. Выражения. Преобразование типов.
- •Лабораторная работа. Операторы языка c#
- •Лабораторная работа. Методы: основные понятия
- •Лабораторная работа. Рекурсивные методы
- •I. Разработать рекурсивный метод (возвращающий значение):
- •II. Разработка рекурсивных методов ( не возвращающих значений):
- •Лабораторная работа. Обработка исключений
- •Лабораторная работа. Массивы
- •I. Дана последовательность целых чисел.
- •II. Дана последовательность из n действительных чисел.
- •III. Дан массив размером n×n, элементы которого целые числа.
- •IV. Дан массив размером n×n, элементы которого целые числа.
- •Лабораторная работа. Символы и строки
- •Лабораторная работа. Регулярные выражения
- •Лабораторная работа. Организация с#-системы ввода-вывода
- •I. Работа с двоичными файлами:
- •II. Работа с текстовым (символьным) файлом.
- •Лабораторная работа. Работа с файловой системой
- •Лабораторная работа. Классы: основные понятия, данные, методы, конструкторы, свойства
- •Лабораторная работа. Классы: деструкторы, индексаторы, операции класса, операции преобразования типов
- •Лабораторная работа. Иерархия классов
- •Лабораторная работа. Интерфейсы и структуры
- •Лабораторная работа. Коллекции пространства имен System.Collection
- •Решить следующие задачи с использованием класса Stack:
- •Решить следующие задачи с использованием класса Queue:
- •Решить задачи из предыдущей лабораторной работы, используя класс ArrayList.
- •Лабораторная работа. Делегаты
- •Лабораторная работа. Сборки .Net
- •Лабораторная работа. Многопоточность
- •Лабораторная работа. Парелельное программирование и библиотека tpl.
- •Лабораторная работа. Сетевое программирование c#.
- •Лабораторная работа. Windows Universal Platform.
- •Лабораторная работа. Wup - проект.
- •Лабораторная работа. Ado.Net работа с базами данных
- •1. Библиотека
- •2. Университет
- •3. Оптовая база
- •4. Производство
- •5. Сеть магазинов
- •6. Авторемонтные мастерские
- •7. Деканат
- •8. Договорная деятельность организации
- •9. Поликлиника
- •10. Телефонная станция
- •11. Спорт
- •12. Сельскохозяйственные работы
- •13. Городской транспорт
- •14. География
- •15. Домоуправление
- •16. Аэропорт
- •Лабораторная работа. Entity Framework
- •1. Библиотека
- •2. Университет
- •3. Оптовая база
- •4. Производство
- •5. Сеть магазинов
- •6. Авторемонтные мастерские
- •7. Деканат
- •8. Договорная деятельность организации
- •9. Поликлиника
- •10. Телефонная станция
- •11. Спорт
- •12. Сельскохозяйственные работы
- •13. Городской транспорт
- •14. География
- •15. Домоуправление
- •16. Аэропорт
- •Лабораторная работа. Xamarin Forms
- •Лабораторная работа. Asp.Net mvc 5 / Core
Делегаты, события и лямбды
Делегаты
Кроме свойств и методов классы и интерфейсы могут содержать делегаты и события. Делегаты представляют такие объекты, которые указывают на другие методы. То есть делегаты - это указатели на методы. С помощью делегатов мы можем вызвать определенные методы в ответ на некоторые произошедшие действия. То есть, по сути, делегаты раскрывают нам функционал функций обратного вызова.
Методы, на которые ссылаются делегаты, должны иметь те же параметры и тот же тип возвращаемого значения. Создадим два делегата:
delegate int Operation(int x, int y);
delegate void GetMessage();
Для объявления делегата используется ключевое слово delegate, после которого идет возвращаемый тип, название и параметры. Первый делегат ссылается на функцию, которая в качестве параметров принимает два значения типа int и возвращает некоторое число. Второй делегат ссылается на метод без параметров, который ничего не возвращает.
Чтобы использовать делегат, нам надо создать его объект с помощью конструктора, в который мы передаем адрес метода, вызываемого делегатом. Чтобы вызвать метод, на который указывает делегат, надо использовать его метод Invoke. Кроме того, делегаты могут выполняться в асинхронном режиме, при этом нам не надо создавать второй поток, нам надо лишь вместо метода Invoke использовать пару методов BeginInvoke/EndInvoke:
class Program
{
delegate void GetMessage(); // 1. Объявляем делегат
static void Main(string[] args)
{
GetMessage del; // 2. Создаем переменную делегата
if (DateTime.Now.Hour < 12)
{
del = GoodMorning; // 3. Присваиваем этой переменной адрес метода
}
else
{
del = GoodEvening;
}
del.Invoke(); // 4. Вызываем метод
Console.ReadLine();
}
private static void GoodMorning()
{
Console.WriteLine("Good Morning");
}
private static void GoodEvening()
{
Console.WriteLine("Good Evening");
}
}
С помощью свойства DateTime.Now.Hour получаем текущий час. И в зависимости от времени в делегат передается адрес определенного метода. Обратите внимание, что методы эти имеют то же возвращаемое значение и тот же набор параметров (в данном случае отсутствие параметров), что и делегат.
Посмотрим на примере другого делегата:
class Program
{
delegate int Operation(int x, int y);
static void Main(string[] args)
{
// присваивание адреса метода через контруктор
Operation del = new Operation(Add); // делегат указывает на метод Add
int result = del.Invoke(4, 5);
Console.WriteLine(result);
del = Multiply; // теперь делегат указывает на метод Multiply
result = del.Invoke(4, 5);
Console.WriteLine(result);
Console.Read();
}
private static int Add(int x, int y)
{
return x + y;
}
private static int Multiply(int x, int y)
{
return x * y;
}
}
Здесь описан способ присваивания делегату адреса метода через конструктор. И поскольку связанный метод, как и делегат, имеет два параметра, то при вызове делегата в метод Invoke мы передаем два параметра. Кроме того, так как метод возвращает значение типа int, то мы можем присвоить результат работы метода Invoke какой-нибудь переменной.
Метод Invoke() при вызове делегата можно опустить и использовать сокращенную форму:
del = Multiply; // теперь делегат указывает на метод Multiply
result = del(4, 5);
То есть делегат можно вызывать как обычный метод, передавая ему аргументы.
Также делегаты могут быть параметрами методов:
class Program
{
delegate void GetMessage();
static void Main(string[] args)
{
if (DateTime.Now.Hour < 12)
{
Show_Message(GoodMorning);
}
else
{
Show_Message(GoodEvening);
}
Console.ReadLine();
}
private static void Show_Message(GetMessage _del)
{
_del.Invoke();
}
private static void GoodMorning()
{
Console.WriteLine("Good Morning");
}
private static void GoodEvening()
{
Console.WriteLine("Good Evening");
}
}
Данные примеры, возможно, не показывают истинной силы делегатов, так как нужные нам методы в данном случае мы можем вызвать и напрямую без всяких делегатов. Однако наиболее сильная сторона делегатов состоит в том, что они функционал методов обратного вызова, уведомляя другие объекты о произошедших событиях.
Рассмотрим другой пример. Пусть у нас есть класс, описывающий счет в банке:
class Account
{
int _sum; // Переменная для хранения суммы
int _percentage; // Переменная для хранения процента
public Account(int sum, int percentage)
{
_sum = sum;
_percentage = percentage;
}
public int CurrentSum
{
get { return _sum; }
}
public void Put(int sum)
{
_sum += sum;
}
public void Withdraw(int sum)
{
if (sum <= _sum)
{
_sum -= sum;
}
}
public int Percentage
{
get { return _percentage; }
}
}
Допустим, в случае вывода денег с помощью метода Withdraw нам надо как-то уведомлять об этом самого клиента и, может быть, другие объекты. Для этого создадим делегат AccountStateHandler. Чтобы использовать делегат, нам надо создать переменную этого делегата, а затем присвоить ему метод, который будет вызываться делегатом.
Итак, добавим в класс Account следующие строки:
class Account
{
// Объявляем делегат
public delegate void AccountStateHandler(string message);
// Создаем переменную делегата
AccountStateHandler del;
// Регистрируем делегат
public void RegisterHandler(AccountStateHandler _del)
{
del = _del;
}
// Далее остальные строки класса Account
}
Здесь фактически проделываются те же шаги, что были выше, и есть практически все кроме вызова делегата. В данном случае у нас делегат принимает параметр типа string. Теперь изменим метод Withdraw следующим образом:
public void Withdraw(int sum)
{
if (sum <= _sum)
{
_sum -= sum;
if (del != null)
del("Сумма " + sum.ToString() + " снята со счета");
}
else
{
if (del != null)
del("Недостаточно денег на счете");
}
}
Теперь при снятии денег через метод Withdraw мы сначала проверяем, имеет ли делегат ссылку на какой-либо метод (иначе он имеет значение null). И если метод установлен, то вызываем его, передавая соответствующее сообщение в качестве параметра.
Теперь протестируем класс в основной программе:
class Program
{
static void Main(string[] args)
{
// создаем банковский счет
Account account = new Account(200, 6);
// Добавляем в делегат ссылку на метод Show_Message
// а сам делегат передается в качестве параметра метода RegisterHandler
account.RegisterHandler(new Account.AccountStateHandler(Show_Message));
// Два раза подряд пытаемся снять деньги
account.Withdraw(100);
account.Withdraw(150);
Console.ReadLine();
}
private static void Show_Message(String message)
{
Console.WriteLine(message);
}
}
Запустив программу, мы получим два разных сообщения:
Сумма 100 снята со счета
Недостаточно денег на счете
Таким образом, мы создали механизм обратного вызова для класса Account, который срабатывает в случае снятия денег. Поскольку делегат объявлен внутри класса Account, то чтобы к нему получить доступ, используется выражениеAccount.AccountStateHandler.
Опять же может возникнуть вопрос: почему бы к коде метода Withdraw() не выводить сообщение о снятии денег? Зачем нужно задействовать какой-то делегат?
Дело в том, что не всегда у нас есть доступ к коду классов. Например, часть классов может создаваться и компилироваться одним человеком, который не будет знать, как эти классы будут использоваться. А использовать эти классы будет другой разработчик.
Так, здесь мы выводим сообщение на консоль. Однако для класса Account не важно, как это сообщение выводится. Классу Account даже не известно, что вообще будет делаться в результате списания денег. Он просто посылает уведомление об этом через делегат.
В результате, если мы создаем консольное приложение, мы можем через делегат выводить сообщение на консоль. Если мы создаем графическое приложение Windows Forms или WPF, то можно выводить сообщение в виде графического окна. А можно не просто выводить сообщение. А, например, записать при списании информацию об этом действии в файл или отправить уведомление на электронную почту. В общем любыми способами обработать вызов делегата. И способ обработки не будет зависеть от класса Account.
Хотя в примере наш делегат принимал адрес на одним метод, в действительности он может указывать сразу на несколько методов. Кроме того, при необходимости мы можем удалить ссылки на адреса определенных методов, чтобы они не вызывались при вызове делегата. Итак, изменим в классе Account метод RegisterHandler и добавим новый метод UnregisterHandler, который будет удалять методы из списка методов делегата:
// Регистрируем делегат
public void RegisterHandler(AccountStateHandler _del)
{
Delegate mainDel = System.Delegate.Combine(_del, del);
del = mainDel as AccountStateHandler;
}
// Отмена регистрации делегата
public void UnregisterHandler(AccountStateHandler _del)
{
Delegate mainDel = System.Delegate.Remove(del, _del);
del = mainDel as AccountStateHandler;
}
В первом методе метод Combine объединяет делегаты _del и del в один, который потом присваивается переменной del. Во втором методе метод Remove возвращает делегат, из списка вызовов которого удален делегат _del. Теперь перейдем к основной программе:
class Program
{
static void Main(string[] args)
{
Account account = new Account(200, 6);
Account.AccountStateHandler colorDelegate = new Account.AccountStateHandler(Color_Message);
// Добавляем в делегат ссылку на методы
account.RegisterHandler(new Account.AccountStateHandler(Show_Message));
account.RegisterHandler(colorDelegate);
// Два раза подряд пытаемся снять деньги
account.Withdraw(100);
account.Withdraw(150);
// Удаляем делегат
account.UnregisterHandler(colorDelegate);
account.Withdraw(50);
Console.ReadLine();
}
private static void Show_Message(String message)
{
Console.WriteLine(message);
}
private static void Color_Message(string message)
{
// Устанавливаем красный цвет символов
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
// Сбрасываем настройки цвета
Console.ResetColor();
}
}
В целях тестирования мы создали еще один метод - Color_Message, который выводит то же самое сообщение только красным цветом. Для первого делегата создается отдельная переменная. Но большой разницы между передачей обоих в метод account.RegisterHandler нет: просто в одном случае мы сразу передаем объект, создаваемый конструктором account.RegisterHandler(new Account.AccountStateHandler(Show_Message));
Во втором случае создаем переменную и ее уже передаем в метод account.RegisterHandler(colorDelegate);.
В строке account.UnregisterHandler(colorDelegate); этот метод удаляется из списка вызовов делегата, поэтому этот метод больше не будет срабатывать. Консольный вывод будет иметь следующую форму:
Сумма 150 снята со счета
Сумма 150 снята со счета
Недостаточно денег на счете
Недостаточно денег на счете
Сумма 50 снята со счета
Также мы можем использовать сокращенную форму добавления и удаления делегатов. Для этого перепишем методы RegisterHandler и UnregisterHandler следующим образом:
// Регистрируем делегат
public void RegisterHandler(AccountStateHandler _del)
{
del += _del; // добавляем делегат
}
// Отмена регистрации делегата
public void UnregisterHandler(AccountStateHandler _del)
{
del -= _del; // удаляем делегат
}
