- •1. Лекция: Visual Studio .Net, Framework .Net
- •Visual Studio .Net - открытая среда разработки
- •Открытость
- •Framework .Net - единый каркас среды разработки
- •Библиотека классов FCL - статический компонент каркаса
- •Единство каркаса
- •Встроенные примитивные типы
- •Структурные типы
- •Архитектура приложений
- •Модульность
- •Общеязыковая исполнительная среда CLR - динамический компонент каркаса
- •Двухэтапная компиляция. Управляемый модуль и управляемый код
- •Виртуальная машина
- •Дизассемблер и ассемблер
- •Метаданные
- •Сборщик мусора - Garbage Collector - и управление памятью
- •Исключительные ситуации
- •События
- •Общие спецификации и совместимые модули
- •2. Лекция: Язык C# и первые проекты
- •Создание C#
- •Виды проектов
- •Консольный проект
- •Windows-проект
- •Начало начал - точка "большого взрыва"
- •Выполнение проекта по умолчанию после "большого взрыва"
- •Проект WindowsHello
- •3. Лекция: Система типов языка С#
- •Общий взгляд
- •Система типов
- •Типы или классы? И типы, и классы
- •Семантика присваивания
- •Преобразование к типу object
- •Примеры преобразований
- •Семантика присваивания. Преобразования между ссылочными и значимыми типами
- •Операции "упаковать" и "распаковать" (boxing и unboxing).
- •4. Лекция: Преобразования типов
- •Где, как и когда выполняются преобразования типов?
- •Преобразования ссылочных типов
- •Преобразования типов в выражениях
- •Преобразования внутри арифметического типа
- •Явные преобразования
- •Преобразования строкового типа
- •Преобразования и класс Convert
- •Проверяемые преобразования
- •Исключения и охраняемые блоки. Первое знакомство
- •Опасные вычисления в охраняемых проверяемых блоках
- •Опасные вычисления в охраняемых непроверяемых блоках
- •Опасные преобразования и методы класса Convert
- •5. Лекция: Переменные и выражения
- •Объявление переменных
- •Проект Variables
- •Синтаксис объявления
- •Время жизни и область видимости переменных
- •Поля
- •Локальные переменные
- •Глобальные переменные уровня процедуры. Существуют ли?
- •Константы
- •6. Лекция: Выражения. Операции в выражениях
- •Выражения
- •Приоритет и порядок выполнения операций
- •Перегрузка операций
- •С чего начинается выполнение выражения
- •Операции "увеличить" и "уменьшить" (increment, decrement)
- •Операции sizeof и typeof
- •Как получить подробную информацию о классе?
- •Статические поля и методы арифметических классов
- •Операция new
- •Арифметические операции
- •Операции отношения
- •Операции проверки типов
- •Операции сдвига
- •Логические операции
- •Условное выражение
- •Операция приведения к типу
- •7. Лекция: Присваивание и встроенные функции
- •Присваивание
- •Специальные случаи присваивания
- •Определенное присваивание
- •Еще раз о семантике присваивания
- •Рассмотрим объявления:
- •Класс Math и его функции
- •Класс Random и его функции
- •8. Лекция: Операторы языка C#
- •Операторы языка C#
- •Оператор присваивания
- •Блок или составной оператор
- •Пустой оператор
- •Операторы выбора
- •Оператор if
- •Оператор switch
- •Операторы перехода
- •Оператор goto
- •Операторы break и continue
- •Оператор return
- •Операторы цикла
- •Оператор for
- •Циклы While
- •Цикл foreach
- •9. Лекция: Процедуры и функции - методы класса
- •Процедуры и функции - функциональные модули
- •Процедуры и функции - методы класса
- •Процедуры и функции. Отличия
- •Описание методов (процедур и функций). Синтаксис
- •Список формальных аргументов
- •Тело метода
- •Вызов метода. Синтаксис
- •О соответствии списков формальных и фактических аргументов
- •Вызов метода. Семантика
- •Что нужно знать о методах?
- •Почему у методов мало аргументов?
- •Поля класса или функции без аргументов?
- •Пример: две версии класса Account
- •Функции с побочным эффектом
- •Методы. Перегрузка
- •10. Лекция: Корректность методов. Рекурсия
- •Корректность методов
- •Инварианты и варианты цикла
- •Рекурсия
- •Рекурсивное решение задачи "Ханойские башни"
- •Быстрая сортировка Хоара
- •11. Лекция: Массивы языка C#
- •Общий взгляд
- •Объявление массивов
- •Объявление одномерных массивов
- •Динамические массивы
- •Многомерные массивы
- •Массивы массивов
- •Процедуры и массивы
- •12. Лекция: Класс Array и новые возможности массивов
- •Класс Array
- •Массивы как коллекции
- •Сортировка и поиск. Статические методы класса Array
- •Класс Object и массивы
- •Массивы объектов
- •Массивы. Семантика присваивания
- •13. Лекция: Символы и строки постоянной длины в C#
- •Общий взгляд
- •Строки С++
- •Строки С#
- •Класс char
- •Класс char[] - массив символов
- •Существует ли в C# тип char*
- •14. Лекция: Строки C#. Классы String и StringBuilder
- •Класс String
- •Объявление строк. Конструкторы класса string
- •Операции над строками
- •Строковые константы
- •Неизменяемый класс string
- •Статические свойства и методы класса String
- •Метод Format
- •Методы Join и Split
- •Динамические методы класса String
- •Класс StringBuilder - построитель строк
- •Объявление строк. Конструкторы класса StringBuilder
- •Операции над строками
- •Основные методы
- •Емкость буфера
- •15. Лекция: Регулярные выражения
- •Пространство имен RegularExpression и классы регулярных выражений
- •Немного теории
- •Синтаксис регулярных выражений
- •Классы Group и GroupCollection
- •Классы Capture и CaptureCollection
- •Перечисление RegexOptions
- •Класс RegexCompilationInfo
- •Примеры работы с регулярными выражениями
- •Пример "чет и нечет"
- •Пример "око и рококо"
- •Пример "кок и кук"
- •Пример "обратные ссылки"
- •Пример "Дом Джека"
- •Пример "Атрибуты"
- •16. Лекция: Классы
- •Классы и ООП
- •Две роли классов
- •Синтаксис класса
- •Поля класса
- •Доступ к полям
- •Методы класса
- •Доступ к методам
- •Методы-свойства
- •Индексаторы
- •Операции
- •Статические поля и методы класса
- •Константы
- •Конструкторы класса
- •Деструкторы класса
- •Проектирование класса Rational
- •Свойства класса Rational
- •Конструкторы класса Rational
- •Методы класса Rational
- •Закрытый метод НОД
- •Печать рациональных чисел
- •Тестирование создания рациональных чисел
- •Операции над рациональными числами
- •Константы класса Rational
- •17. Лекция: Структуры и перечисления
- •Развернутые и ссылочные типы
- •Классы и структуры
- •Структуры
- •Синтаксис структур
- •Класс Rational или структура Rational
- •Встроенные структуры
- •Еще раз о двух семантиках присваивания
- •Перечисления
- •Персоны и профессии
- •18. Лекция: Отношения между классами. Клиенты и наследники
- •Отношения между классами
- •Отношения "является" и "имеет"
- •Отношение вложенности
- •Расширение определения клиента класса
- •Отношения между клиентами и поставщиками
- •Сам себе клиент
- •Наследование
- •Добавление полей потомком
- •Конструкторы родителей и потомков
- •Добавление методов и изменение методов родителя
- •Статический контроль типов и динамическое связывание
- •Три механизма, обеспечивающие полиморфизм
- •Пример работы с полиморфным семейством классов
- •Абстрактные классы
- •Классы без потомков
- •19. Лекция: Интерфейсы. Множественное наследование
- •Интерфейсы
- •Две стратегии реализации интерфейса
- •Преобразование к классу интерфейса
- •Проблемы множественного наследования
- •Коллизия имен
- •Наследование от общего предка
- •Встроенные интерфейсы
- •Упорядоченность объектов и интерфейс IComparable
- •Клонирование и интерфейс ICloneable
- •Сериализация объектов
- •Класс с атрибутом сериализации
- •Интерфейс ISerializable
- •20. Лекция: Функциональный тип в C#. Делегаты
- •Как определяется функциональный тип и как появляются его экземпляры
- •Функции высших порядков
- •Вычисление интеграла
- •Построение программных систем методом "раскрутки". Функции обратного вызова
- •Наследование и полиморфизм - альтернатива обратному вызову
- •Делегаты как свойства
- •Операции над делегатами. Класс Delegate
- •Операции "+" и "-"
- •Пример "Комбинирование делегатов"
- •Пример "Плохая служба"
- •21. Лекция: События
- •Классы с событиями
- •Класс sender. Как объявляются события?
- •Делегаты и события
- •Как зажигаются события
- •Классы receiver. Как обрабатываются события
- •Классы с событиями, допустимые в каркасе .Net Framework
- •Пример "Списки с событиями"
- •Класс sender
- •Классы receiver
- •Две проблемы с обработчиками событий
- •Игнорирование коллег
- •Переопределение значений аргументов события
- •Классы с большим числом событий
- •Проект "Город и его службы"
- •22. Лекция: Универсальность. Классы с родовыми параметрами
- •Наследование и универсальность
- •Синтаксис универсального класса
- •Класс с универсальными методами
- •Два основных механизма объектной технологии
- •Стек. От абстрактного, универсального класса к конкретным версиям
- •Ограниченная универсальность
- •Синтаксис ограничений
- •Список с возможностью поиска элементов по ключу
- •Как справиться с арифметикой
- •Родовое порождение класса. Предложение using
- •Универсальность и специальные случаи классов
- •Универсальные структуры
- •Универсальные интерфейсы
- •Универсальные делегаты
- •Framework .Net и универсальность
- •23. Лекция: Отладка и обработка исключительных ситуаций
- •Корректность и устойчивость программных систем
- •Жизненный цикл программной системы
- •Три закона программотехники
- •Первый закон (закон для разработчика)
- •Второй закон (закон для пользователя)
- •Третий закон (закон чечако)
- •Отладка
- •Создание надежного кода
- •Искусство отладки
- •Отладочная печать и условная компиляция
- •Классы Debug и Trace
- •Метод Флойда и утверждения Assert
- •Классы StackTrace и BooleanSwitch
- •Отладка и инструментальная среда Visual Studio .Net
- •Обработка исключительных ситуаций
- •Обработка исключений в языках C/C++
- •Схема обработки исключений в C#
- •Выбрасывание исключений. Создание объектов Exception
- •Захват исключения
- •Параллельная работа обработчиков исключений
- •Блок finally
- •Схема Бертрана обработки исключительных ситуаций
- •Класс Exception
- •Организация интерфейса
- •Форма и элементы управления
- •Взаимодействие форм
- •Модальные и немодальные формы
- •Передача информации между формами
- •Образцы форм
- •Главная кнопочная форма
- •Шаблон формы для работы с классом
- •Работа со списками (еще один шаблон)
- •Элемент управления класса ListBox
- •Наследование форм
- •Два наследника формы TwoLists
- •Огранизация меню в формах
- •Создание меню в режиме проектирования
- •Классы меню
- •Создание инструментальной панели с командными кнопками
- •Рисование в форме
- •Класс Graphics
- •Методы класса Graphics
- •Класс Pen
- •Класс Brush
- •Проект "Паутина Безье, кисти и краски"
- •Паутина Безье
- •Событие Paint
- •Кисти и краски
- •Абстрактный класс Figure
- •Классы семейства геометрических фигур
- •Класс Ellipse
- •Класс Circle
- •Класс LittleCircle
- •Класс Rect
- •Класс Square
- •Класс Person
- •Список с курсором. Динамические структуры данных
- •Классы элементов списка
- •Организация интерфейса
Итак, если событие имеет аргументы, то все входные аргументы должны быть закрыты для обработчиков события. Если обработчиков несколько, то лучше или не использовать выходных аргументов, или аккуратно запрограммировать логику обработчиков, которая учитывает решения, полученные коллегами - ранее отработавшими обработчиками события.
Классы с большим числом событий
Как было сказано, каждое событие класса представляется полем этого класса. Если у класса много объявленных событий, а реально возникает лишь малая часть из них, то предпочтительнее динамический подход, когда память отводится только фактически возникшим событиям. Это несколько замедляет время выполнения, но экономит память. Решение зависит от того, что в данном контексте важнее - память или время. Для реализации динамического подхода в языке предусмотрена возможность задания пользовательских методов Add и Remove в момент объявления события. Это и есть другая форма объявления события, упоминавшаяся ранее. Вот ее примерный синтаксис:
public event <Имя Делегата> <Имя события>
{
add {...} remove {...}
}
Оба метода должны быть реализованы, при этом для хранения делегатов используется некоторое хранилище. Именно так реализованы классы для большинства интерфейсных объектов, использующие хэш-таблицы для хранения делегатов.
Давайте построим небольшой пример, демонстрирующий такой способ объявления и работы с событиями. Вначале построим класс с несколькими событиями:
class ManyEvents
{
//хэш таблица для хранения делегатов
Hashtable DStore = new Hashtable(); public event EventHandler Ev1
{
add
{
DStore["Ev1"]= (EventHandler)DStore["Ev1"]+ value;
}
remove
{
DStore["Ev1"]= (EventHandler)DStore["Ev1"]- value;
}
}
public event EventHandler Ev2
{
add
{
DStore["Ev2"]= (EventHandler)DStore["Ev2"]+ value;
}
remove
{
DStore["Ev2"]= (EventHandler)DStore["Ev2"]- value;
}
}
public event EventHandler Ev3
{
add
{
DStore["Ev3"]= (EventHandler)DStore["Ev3"]+ value;
}
remove
{
DStore["Ev3"]= (EventHandler)DStore["Ev3"]- value;
}
}
public event EventHandler Ev4
{
add
{
DStore["Ev4"]= (EventHandler)DStore["Ev4"]+ value;
}
remove
{
DStore["Ev4"]= (EventHandler)DStore["Ev4"]- value;
}
}
public void SimulateEvs()
{
EventHandler ev = (EventHandler) DStore["Ev1"]; if(ev != null) ev(this, null);
ev = (EventHandler) DStore["Ev3"]; if(ev != null) ev(this, null);
}
}//class ManyEvents
В нашем классе созданы четыре события и хэш-таблица DStore для их хранения. Все события принадлежат встроенному классу EventHandler. Когда к событию будет присоединяться обработчик, автоматически будет вызван метод add, который динамически создаст элемент хэш-таблиц. Ключом элемента является, в данном случае, строка с именем события. При отсоединении обработчика будет исполняться метод remove, выполняющий аналогичную операцию над соответствующим элементом хэштаблицы. В классе определен также метод SimulateEvs, при вызове которого зажигаются два из четырех событий - Ev1 и Ev3.
Рассмотрим теперь класс ReceiverEvs, слушающий события. Этот класс построен по описанным ранее правилам. В нем есть ссылка на класс, создающий события; конструктор с параметром, которому передается реальный объект такого класса; четыре обработчика события - по одному на каждое, и метод OnConnect, связывающий обработчиков с событиями. Вот код класса:
class ReceiverEvs
{
private ManyEvents manyEvs;
public ReceiverEvs( ManyEvents manyEvs)
{
this.manyEvs = manyEvs; OnConnect();
}
public void OnConnect()
{
manyEvs.Ev1 += new EventHandler(H1); manyEvs.Ev2 += new EventHandler(H2); manyEvs.Ev3 += new EventHandler(H3); manyEvs.Ev4 += new EventHandler(H4);
}
public void H1(object s, EventArgs e)
{
Console.WriteLine("Событие Ev1");
}
public void H2(object s, EventArgs e)
{
Console.WriteLine("Событие Ev2");
}
public void H3(object s, EventArgs e)
{
Console.WriteLine("Событие Ev3");
}
public void H4(object s, EventArgs e)
{
Console.WriteLine("Событие Ev4");
}
}//class ReceiverEvs
Тестирующая процедура состоит из нескольких строчек, в которых создаются нужные объекты и запускается метод Simulate, зажигающий события:
public void TestManyEvents()
{
ManyEvents me = new ManyEvents(); ReceiverEvs revs = new ReceiverEvs(me); me.SimulateEvs();
}
Все работает предусмотренным образом.
Проект "Город и его службы"
Завершить лекцию о событиях хочется содержательным учебным проектом, в котором моделируется жизнь города, происходящие в нем события и реакция на них городских служб. Наша главная цель в данном проекте - еще раз показать, как возникающее событие, в данном случае - пожар в одном из домов города, обрабатывается поразному городскими службами - пожарными, милицией, скорой помощью. Конечно, все упрощено, в реальном городе событиями являются не только пожары и преступления, но и более приятные ситуации: день города, открытие фестивалей и выставок, строительство новых театров и институтов.
Начнем с описания класса, задающего наш город. Этот класс уже появлялся и в этой, и в предыдущей лекции, здесь его описание будет расширено. Начнем со свойств класса:
public class NewTown
{
//свойства
private int build, BuildingNumber; //дом и число домов в городе
private int day, days; |
//Текущий день года |
//городские службы |
|
private Police policeman ; |
|
private Ambulance ambulanceman ; |
|
private FireDetect fireman ; |
|
//события в городе |
|
public event FireEventHandler Fire; |
|
//моделирование случайных событий private Random rnd = new Random();
//вероятность пожара в доме в текущий день: p= m/n private int m = 3, n= 10000;
В нашем городе есть дома; есть время, текущее день за днем; городские службы; событие "пожар", которое, к сожалению, может случайно с заданной вероятностью возникать каждый день в каждом доме. Рассмотрим конструктор объектов нашего класса:
//конструктор класса
public NewTown(int TownSize, int Days)
{
BuildingNumber = rnd.Next(TownSize); days = Days;
policeman = new Police(this); ambulanceman= new Ambulance(this); fireman= new FireDetect(this); policeman.On(); ambulanceman.On();
fireman.On();
}
При создании объектов этого класса задается размер города - число его домов и период времени, в течение которого будет моделироваться жизнь города. При создании объекта создаются его службы - объекты соответствующих классов Police, Ambulance, FireDetect, которым предается ссылка на сам объект "город". При создании служб вызываются методы On, подключающие обработчики события Fire каждой из этих служб к событию.
В соответствии с ранее описанной технологией определим метод OnFire, включающий событие:
protected virtual void OnFire(FireEventArgs e)
{
if(Fire != null) Fire(this, e);
}
Где и когда будет включаться событие Fire? Напишем метод, моделирующий жизнь города, где для каждого дома каждый день будет проверяться, а не возник ли пожар, и, если это случится, то будет включено событие Fire:
public void LifeOurTown()
{
for(day = 1; day<=days; day++)
for(build =1; build <= BuildingNumber; build++)
{
if( rnd.Next(n) <=m) //загорелся дом
{
//аргументы события
FireEventArgs e = new FireEventArgs(build, day, true); OnFire(e);
if(e.Permit)
Console.WriteLine("Пожар потушен!" + " Ситуация нормализована.");
else Console.WriteLine("Пожар продолжается." + " Требуются дополнительные средства!");
}
}
}
Рассмотрим теперь классы Receiver, обрабатывающие событие Fire. Их у нас три, по одному на каждую городскую службу. Все три класса устроены по одному образцу. Напомню, каждый такой разумно устроенный класс, кроме обработчика события, имеет конструктор, инициализирующий ссылку на объект, создающий события, методы подключения и отсоединения обработчика от события. В такой ситуации целесообразно построить вначале абстрактный класс Receiver, в котором будет предусмотрен обработчик события, но не задана его реализация, а затем для каждой службы построить класс-потомок. Начнем с описания родительского класса:
public abstract class Receiver
{
private NewTown town;
public Receiver(NewTown town) {this.town = town;}
public void On()
{
town.Fire += new FireEventHandler(It_is_Fire);
}
public void Off()
{
town.Fire -= new FireEventHandler(It_is_Fire); town = null;
}
public abstract void It_is_Fire(object sender, FireEventArgs e);
}//class Receiver
Для классов потомков абстрактный метод It_is_Fire будет определен. Вот их описания:
public class Police : Receiver
{
public Police (NewTown town): base(town){} public override void It_is_Fire(object sender,
FireEventArgs e)
{
Console.WriteLine("Пожар в доме {0}. День {1}-й." + " Милиция ищет виновных!", e.Build,e.Day);
e.Permit &= true;
}
}// class Police
public class FireDetect : Receiver
{
public FireDetect (NewTown town): base(town){}
public override void It_is_Fire(object sender, FireEventArgs e)
{
Console.WriteLine("Пожар в доме {0}. День {1}-й."+ " Пожарные тушат пожар!", e.Build,e.Day);
Random rnd = new Random(e.Build); if(rnd.Next(10) >5)
e.Permit &= false; else e.Permit &=true;
}
}// class FireDetect
public class Ambulance : Receiver
{
public Ambulance(NewTown town): base(town){} public override void It_is_Fire(object sender,
FireEventArgs e)
{
Console.WriteLine("Пожар в доме {0}. День {1}-й."+ " Скорая спасает пострадавших!", e.Build,e.Day);
e.Permit &= true;
}
}// class Ambulance
Для каждого потомка задан конструктор, вызывающий базовый метод родителя. Каждый потомок по-своему определяет обработчика события Fire. Обратите внимание на то, как в данном проекте решается проблема с выходным параметром события - Permit. Принята следующая стратегия: возвращаемое значение Permit будет истинно, если все обработчики согласны с этим. Поэтому каждый обработчик использует
конъюнкцию выработанного им значения со значением, пришедшим от предыдущего обработчика. В данном примере все зависит от пожарных, которые могут вырабатывать разные решения.
Для полноты картины необходимо показать, как выглядит класс, задающий аргументы события, который, как и положено, является потомком класса EventArgs:
public class FireEventArgs : EventArgs
{
private int build; private int day; private bool permit; public int Build
{
get{ return(build);} ///set{ build = value;}
}
public int Day
{
get{ return(day);} ///set{ day = value;}
}
public bool Permit
{
get{ return(permit);} set{ permit = value;}
}
public FireEventArgs(int build, int day, bool permit)
{
this.build = build; this.day = day; this.permit = permit;
}
}//class FireEventArgs
Входные параметры события - build и day защищены от обработчиков события, а корректность выходного параметра гарантируется тщательным программированием самих обработчиков.
Для завершения проекта нам осталось определить тестирующую процедуру в классе Testing, создающую объекты и запускающую моделирование жизни города:
public void TestLifeTown()
{
NewTown sometown = new NewTown(100,100); sometown.LifeOurTown();
}
Результаты ее работы зависят от случайностей. Вот как выглядит один из экспериментов:
Рис. 21.3. События в жизни города