Тема 9. Объектно-ориентированное программирование

advertisement
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
Тема 9. Объектно-ориентированное программирование
Цель
Рассмотреть принципы программирования на основе событий.
Задачи
1. Познакомиться с особенностями модели программирования на
основе событий (event-driven programming).
2. На примере событий стандартных классов рассмотреть
принципы написания обработчиков событий.
3. Научиться управлять событиями собственных классов.
4. Рассмотреть технологию обработки исключений в среде .NET
Framework.
5. Познакомиться с технологией конструирования программ.
Оглавление
Тема 9. Объектно-ориентированное программирование ................ 1
Понятие события .................................................................................... 1
Обработка событий в программе ........................................................ 3
Разработка событий собственных классов ....................................... 4
Обработка исключений в среде .NET Framework .............................. 8
Конструирование программ: создание библиотеки классов ........ 10
Выводы .................................................................................................. 10
Вопросы для самопроверки ............................................................... 11
Литература ............................................................................................. 11
Понятие события
Одной из наиболее важных особенностей объектно-ориентированного
программирования является возможность использования модели
программирования на основе событий (event-driven programming). Этот
способ не нов, сама среда Windows является средой, управляемой
событиями. Это означает, что в Windows ничего не происходит само
по себе, а только в ответ на нечто (событие), обнаруженное Windows.
Примером таких событий может быть щелчок мыши на ярлыке на
рабочем столе, нажатие клавиши на клавиатуре или открытие меню
Пуск.
На первый взгляд, все понятно, но технология работы с событиями
вовсе не так проста, как кажется. Даже при определении понятия
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
«событие» возникают разногласия. Что такое «событие»? Трудно
сказать. Под событием принято понимать действие, но под событием
часто понимают и сообщение о нем. Твердо можно сказать только
одно, что при инициировании события вызывается соответствующий
метод вашего объекта.
Чем непосредственный вызов метода объекта отличается от вызова
того же метода по событию? С сугубо концептуальной точки зрения –
это всего лишь вопрос восприятия. Рассмотрим два объекта.
Клиентский объект является частью вашей программы и создает
серверный объект для выполнения некоторой задачи (рис. 9.1).
Серверный объект при необходимости может передать клиенту
некоторую информацию. Концептуальное различие между вызовом
метода и метода по событию связано с направлением вызова.
Вызывая метод из своего приложения, вы сами определяете, когда
это
происходит,
то
есть
вызов
входит
в
нормальную
последовательность выполнения программы. События можно
рассматривать как вызовы методов, не входящие в нормальную
последовательность выполнения, на которые ваше приложение
должно определенным образом отреагировать.
Клиент
Непосредственный вызов методов
Сервер
Вызов методов через события
Метод события
Рис. 9.1. Простейшая схема обработки событий
Программирование на основе событий коренным образом изменило
характер
модели
программирования.
Традиционные
языки
программирования выполняют программу последовательно от строки
к строке. Даже если используется функции и процедуры, это не
слишком меняет порядок выполнения: одна процедура вызывает
другую, которая вызывает третью и так далее. Другими словами,
здесь присутствует упорядоченная последовательность действий.
Концепция программирования на основе событий меняет все.
Последовательный способ выполнения не подходит для события. Так,
среда Windows не занимается последовательным выполнением
действий. Если пользователь щелкает мышью в меню, он требует
немедленного появления меню, никто не хочет сидеть и ждать, пока
Windows закончит выполнять то, чем она была занята до этого.
Прежде, чем мы с вами начнем создавать события в своих классах,
рассмотрим, как эти события устроены в стандартных классах и каким
образом следует программировать обработку событий.
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
Обработка событий в программе
Все события, с которыми мы сталкивались до сих пор, относились к
стандартным классам либо Forms, либо Controls (элементы
управления, расположенные на форме).
Наиболее важными событиями стандартных классов Forms и Controls
являются: события Load, Paint, Closed и Closing, происходящие при
загрузке, перерисовке, закрытии форм, события KeyDown, KeyUp и
KeyPress, происходящие при нажатии клавиш клавиатуры, события
MouseDown, MouseUp и MouseMove, происходящие при перемещении
указателя мыши. Сигнатура метода, выполняющего обработку
соответствующего события, имеет, например, следующий вид:
Private Sub Form2_MouseDown(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown,
где sender – объект, который является источником (отправителем)
события, объект e – это объект стандартного класса EventArgs (в
данном случае System.Windows. Forms.MouseEventArgs), который
содержит сведения о произошедшем событии (аргументы события),
Handles MyBase.MouseDown – директива компилятору: какой из
объектов инициирует какое событие.
Объект e имеет свойства, которым присваиваются определенные
значения при выполнении события, например, при нажатии кнопки
мыши в объект e передаются координаты указателя мыши и
состояние кнопок (какая из них нажата/отпущена). Значения свойств
объекта e можно считывать – таким образом, и происходит
программирования реакции на произошедшее событие. Например,
напишем обработчик события нажатия кнопки мыши в форме. Если
событие происходит, то объект e получает информацию о том, какая
из кнопок нажата и координаты указателя мыши в форме: свойства
Button и X, Y соответственно. Значения этих свойств можно прочитать:
Private Sub Form2_MouseDown(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
If e.Button = MouseButtons.Left Then
MsgBox(e.X, e.Y)
End If
End Sub
Другой пример стандартного события. Событие KeyUp происходит,
когда пользователь отпускает любую нажатую клавишу на клавиатуре.
В процедуру обработчик этого события передается параметр e,
являющийся объектом теперь другого стандартного класса –
System. Windows. Forms. KeyEventArgs и имеющий другие свойства:
Alt, Control, Handler, KeyCode и Shift. Эти свойства позволяют узнать
код отжатой клавиши и состояние специальных клавиш Control, Alt,
Shift.
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
Private Sub Form2_KeyUp(ByVal sender As Object, ByVal e As
System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyUp
If e.KeyCode = Keys.Insert Then MsgBox("Была нажата клавиша Insert")
End Sub
Подобные обработчики событий используются во всех событийных
приложениях, то есть приложениях, поддерживающих модель
программирования на основе событий (event-driven programming).
Кратко рассмотрим архитектуру событий .NET Framework. Объект,
который генерирует событие, известен как источник события.
Например, объект Button является источником событий, потому что он
генерирует такие события, как Click или MouseDown. Источник
событий объявляет события, которые он может генерировать; в
VB.NET мы используем ключевое слово Event для объявления
события в определении класса. Объект, который определяет методыобработчики события, известен как получатель события. Получатель
события должен предоставить метод-обработчик события с
сигнатурой, соответствующей тому событию, для которого он
регистрируется.
Источник события генерирует события при выполнении своих
методов. Например, объекты Button генерируют событие Click, когда
пользователь щелкает мышью на кнопке (экране).
Разработка событий собственных классов
Теперь рассмотрим, каким образом можно добавить события в
собственные классы. В соответствии с принципом инкапсуляции
события являются членами класса и должны быть объявлены в его
определении. Для объявления событий в VB.NET мы должны
объявить члены с ключевым словом Event в классе-источнике событий
(или серверном классе). Мы должны указать два вида информации
для каждого события:
 Имя события. Например, класс Person объявляет событие с
именем InfoUpdate (была добавлена новая информация о
сотруднике). Мы должны объявить члены Event для этого
события
нашего
класса,
поскольку
оно
способно
заинтересовать другие объекты в клиентском приложении.
 Сигнатуру (список параметров) события. Когда объектисточник события генерирует событие, он предоставит
значения для этих параметров. Эти значения будут переданы
в метод-обработчик события в объекте-получателе события.
Это подразумевает, что методы-обработчики события должны
иметь такую же сигнатуру, как и событие.
Имеется два способа объявления событий в классе-источнике
события: указать сигнатуру события явно, как часть объявления
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
события и указать сигнатуру события неявно, используя делегата. Мы
с вами ограничимся первым способом, так как понятие «делегат» мы
не рассматриваем. Можно это сделать самостоятельно, пользуясь
литературой, список которой приведен в конце темы. Итак,
рассмотрим явное определение сигнатуры события в собственном (в
данном случае серверном) классе. Можно определять одно событие с
явным заданием сигнатуры, а можно определять несколько событий,
которые имеют одинаковую сигнатуру.
Следующий пример показывает, как определить одно событие с
использованием явного задания его сигнатуры. Пример использует
класс Person и определяет событие с именем InfoUpdate.
Public Class Person
Public Event InfoUpdate (ByVal sender As Object, ByVal e As
MyEventArgs)
Обратите внимание на следующие моменты в этом примере. В
VB.NET мы используем ключевое слово Event для объявления
событий в классе. События – это члены класса, и, следовательно,
имеют модификаторы доступа. По умолчанию они объявляются как
Public, потому что природа событий такова, что они публикуют
информацию для других объектов приложения. Однако если это
необходимо, то существует возможность ограничить область
видимости событий как Private (только для методов нашего класса),
Friend (только для методов сборки), Protected (только для методов
нашего класса и методов классов, наследников нашего класса).
Событие InfoUpdate имеет два параметра. Первый из них дает ссылку
на объект, сгенерировавший событие, а второй (объект MyEventArgs)
инкапсулирует в себе информацию о событии. Для простоты в данном
случае этот объект будет содержать только одно скрытое поле и одно
свойство доступа к нему.
Public Class myEventArgs
Inherits System.EventArgs
Private _m As String
Sub New(ByVal value As String)
MyBase.New()
Me._m = value
End Sub
Public Property Messages() As String
Get
Return _m
End Get
Set(ByVal Value As String)
_m = Value
End Set
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
End Property
End Class
Мы должны будем предоставлять эти параметры в процессе
генерации события. Затем параметры будут переданы в методобработчик события так, что объекты-получатели события получат
некоторое представление о том, о каком сотруднике идет речь и
удалось ли информацию о нем добавить. События не могут иметь
возвращаемого
значения.
Они
представляют
собой
однонаправленный поток информации от объекта-источника события к
объекту-получателю события. Указание возвращаемого значения для
события является ошибкой.
Теперь необходимо описать метод, при выполнении которого будет
происходить событие. Процесс генерации события описывается с
ключевым словом RaiseEvent. Ниже показано как может выглядеть
метод класса, в рамках которого генерируется событие. Например,
происходит проверка на допустимость символов в имени Person.
Public Sub SaveInfo()
Dim c As Char
Dim e As New myEventArgs("Имя ")
For Each c In Me.Name
If Char.IsDigit(c) Then
e.Messages &= "недопустимо"
RaiseEvent InfoUpdate(Me.Name, e)
Exit Sub
End If
Next
e.Messages &= "допустимо"
RaiseEvent InfoUpdate(Me.Name, e)
End Sub
Обратите внимание на следующие моменты: оператор RaiseEvent
требует имя события, которое мы хотим генерировать, за которым
должен следовать список значений параметров, заключенных в
скобки, эти значения будут переданы в метод-обработчик этого
события. Если событие не принимает никаких параметров, мы должны
опустить скобки.
Мы рассмотрели, как объявить событие, теперь рассмотрим, как
объекты получатели события могут «подписаться» на это событие.
Имеется два способа подписаться на событие: определить обработчик
события статически, используя ключевые слова WithEvents и Handles,
определить обработчик события динамически, используя ключевые
слова AddHandler и RemoveHandler. Рассмотрим статическое
определение событий в клиентском классе.
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
Для того чтобы клиент получил сообщение о событии сервера, в
определении объекта серверного класса необходимо указать
ключевое слово WithEvent, при этом область видимости данного
объекта должна быть ограничена классом. После объявления объекта
серверного класса для обработки события можно использовать любой
метод клиентского класса, список аргументов, которого совпадает со
списком, передаваемым сообщением о событии. Ниже представлен
класс формы, который обрабатывает событие серверного класса.
Public Class Form2
Inherits System.Windows.Forms.Form
Dim WithEvents oPerson As Person
Dim dPerson As Person
Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
oPerson = New Person("Федоркин")
dPerson = New Person("1Перовский")
oPerson.SaveInfo()
dPerson.SaveInfo()
End Sub
Private Sub oPerson_InfoUpdate(ByVal sender As Object, ByVal e As
myEventArgs) Handles oPerson.InfoUpdate
MsgBox(e.Messages)
End Sub
End Class
Имеется несколько ограничений на использование ключевого слова
WithEvents. Мы можем использовать WithEvents для типов, которые
генерируют события. Это следует учитывать; незачем обрабатывать
события объекта, который никогда не генерирует событий. Мы можем
использовать WithEvents только для обработки событий типов Class и
Interface. Мы не можем использовать WithEvents для обработки
событий типа Structure. Мы можем использовать WithEvents только
для обработки событий экземпляра. Мы не можем использовать
WithEvents для обработки Shared-событий, потому что механизм
WithEvents требует наличия экземпляра объекта.
Любой метод может принимать любое число событий от любого числа
объектов – при условии, что сигнатура метода соответствует
сигнатуре делегата события. Рассмотрим пример обработки
нескольких событий. В VB.NET массивы элементов управления не
поддерживаются, то есть невозможно, например, по индексу,
определить – какой именно элемент из группы был выбран
пользователем. В приведенном ниже фрагменте три разных
переключателя ассоциируются с одним событием.
Private Sub Options_CheckedChanged (ByVal sender as System.Object,
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
ByVal e As System.EvebtArgs) Handles radioButton3.CheckedChanged,
radioButton2.CheckedChanged, radioButton1.CheckedChanged
Dim rb As RadioButton
Rb=CType(sender, RadioButton)
Console.WriteLine(rb.Name & “ “ & rb.Text)
End Sub
Метод
Options_CheckedChanged
связывает
с
событием
CheckedChanged три разных переключателя, для чего все события
перечисляются после ключевого слова Handles. Параметр sender
указывает, каким переключателем было инициировано событие.
Поскольку массивы элементов в VB.NET не поддерживаются, вам не
удастся идентифицировать источник события по значению свойства
Index. В зависимости от типа приложения для идентификации
источника можно воспользоваться любым другим подходящим
свойством, например свойствами Name, Text или позицией элемента.
Если вы твердо уверены, что все элементы относятся к одному типу,
параметр sender можно объявить с типом соответствующего элемента
и обращаться к свойству напрямую, как показано выше для объекта rb,
объявленного с типом RadioButton (кнопка-переключатель).
Обработка исключений в среде .NET Framework
При создании программ большое значение имеет обработка
исключений (ошибок) – непредвиденных программистом ситуаций,
которые могут возникать при выполнении приложения. Например, это
может быть попытка сохранить информацию в базе данных на
сервере при отсутствии соединения, выход значения индекса массива
за допустимые пределы и так далее.
В среде .NET Framework используется структурированный механизм
обработки исключений, позволяющий делать это без аварийного
завершения программы. К основным преимуществам данного
механизма можно отнести использование единых средств поддержки
обработки исключений во всех языках среды .NET, возможность
создания защищенных блоков кода и фильтрации исключений,
гарантирующих
корректное
завершение
задач
даже
при
возникновении фатальных ошибок. Среда .NET Framework
предоставляет также большое количество встроенных классов
исключений, используемых для обработки стандартных ошибок.
Например, класс FileNotFoundException содержит информацию об
имени файла, сообщение об ошибке и источник исключения, то есть
код, содержащий операторы открытия несуществующего файла.
Кроме того, среда .NET Framework позволяет программисту создавать
собственные классы. Которые используются для обработки
исключений, возникающих во время работы приложения.
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
Для обработки исключений используется конструкция Try Catch Finally
End Try. Обобщенный синтаксис структурной обработки ошибок
выглядит следующим образом:
Try
инструкции блока Try
Catch ИмяОбъекта1 As ТипИсключения1 (ошибки1)
инструкции блока Catch 1, выполняемые при возникновении ошибки
Catch ИмяОбъекта1 As ТипИсключения2 (другой тип ошибки)
инструкции блока Catch 2, выполняемые при возникновении ошибки
Catch ИмяОбъектаN As ТипИсключенияN (другой тип ошибки)
инструкции блока Catch N, выполняемые при возникновении ошибки
Finally
инструкции блока Finally, выполняемые перед выходом из блока Try
End Try
Здесь необязательными являются все блоки Catch, кроме первого, а
также блок Finally. Блок Try (от ключевого слова Try до первого Catсhблока) определяет фрагмент программы, в котором производится
отслеживание ошибок. Если в этом фрагменте будет генерироваться
исключение, запустится процедура поиска подходящего для него
обработчика среди имеющихся Catсh-блоков.
Каждый Catch-блок представляет собой обработчик некоторого типа
исключения. После того, как исключение будет обработано, то есть,
будут выполнены все инструкции соответствующего блока Catch,
управление передается на первую строку блока Finally, а при его
отсутствии – на строку, следующую за инструкцией End Try.
Инструкции блока Finally выполняются всегда – независимо от того,
возникали исключительные ситуации в блоке Try или нет. Блок Finally
обычно используется для освобождения системных ресурсов,
закрытия открытых файлов и так далее.
Внутри каждого блока могут находиться другие блоки, в том числе и
вложенные блоки Try End Try. Различные типы ошибок
представляются объектами исключений (exceptions), производными от
класса System.Exception. Блок Сatch также может содержать секцию
When с указанием дополнительного условия. Блок Catch выполняется
только при возникновении ошибки определенного типа и при
истинности условия, заданного в секции When.
Но важнейшие изменения в области обработки исключений в .NET
связаны не с синтаксисом структуры Try Catch (при всей ее важности)
и даже не с тем, что все ошибки времени выполнения программы
инициируют исключения, перехватываемые механизмом структурной
обработки ошибок. Самое главное заключается в том, что все ошибки,
возникающие во всех методах и свойствах объектов .NET Framework,
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
также инициируют исключения,
структурной обработки ошибок.
перехватываемые
механизмом
Конструирование программ: создание библиотеки классов
Библиотека базовых классов .NET Framework (Base Class Library
Framework) находится на самом верхнем уровне иерархии CLR. Она
содержит классы, обеспечивающие поддержку ссылочных и обычных
типов, инкапсулирующих доступ к функциям системы. Создавая
собственный класс, вы разрабатываете его как наследника
библиотечного класса System.Object, находящегося на самом верхнем
уровне иерархии библиотеки классов. Для того чтобы ваш класс мог
быть использован не только в том проекте, где вы его описали в
модуле Class, но и в других проектах, необходимо создавать описание
класса в отдельном проекте, затем откомпилировать его и
организовать на него ссылку в других проектах.
На основе разработанных классов можно создавать библиотечные
файлы (DLL-файлы, Dynamic Link Library) с повторно используемым
кодом. Рассмотрим технологию создания такого библиотечного
файла.
При создании нового проекта выберите шаблон Class Library, введите
имя проекта, например, MyLib.vb. Введите описание класса, не
забудьте описать NameSpace для вашего класса. Выберите команду
Build – Build MyLib, чтобы откомпилировать созданную библиотеку (в
нашем случае результатом компиляции будет файл MyLib.dll).
Для включения библиотеки в проект выберите команду Project – Add
Reference. Найдите через Browse искомую ссылку на файл с
расширением dll. Не забудьте при обращении к экземпляру класса
указать название библиотеки и пространство имен в этой библиотеке.
Выводы
Все создаваемые в среде .NET Framework приложения являются
объектно-ориентированными, и управление ими осуществляется
путем обработки событий. В VB.NET события являются членами
класса и должны быть объявлены в его определении. Источник
событий объявляет события, которые он может генерировать,
используем для этого ключевое слово Event для объявления события
в определении класса. Получатель события должен предоставить
метод-обработчик события с сигнатурой, соответствующей тому
событию, для которого он регистрируется.
В термине «структурная обработка ошибок» главным словом является
слово «структурная». В языках программирования, имеющих блочную
структуру, программист определяет блоки программного кода,
выполняемые как единое целое. Структурная обработка ошибок
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
расширяет этот принцип в области обработки ошибок. Но даже
организованная структурно, обработка ошибок могла бы не иметь
большой практической пользы, если бы не поддерживалась
библиотеками и объектами той среды, в которой вы программируете.
Все ошибки, возникающие во всех методах и свойствах объектов .NET
Framework, инициируют исключения, перехватываемые механизмом
структурной обработки ошибок – это главное достоинство механизма
Try Catch, реализованного в .NET Framework.
В библиотеке базовых типов .NET Framework имеется тип
System.Object, который является родительским типом для всех
пользовательских
типов,
разрабатываемых
пользователем.
Пользовательские типы наследуют методы и свойства родительского
класса, в дальнейшем переопределяя их под свои требования. Для
того, чтобы созданная вами библиотека стала общим достоянием и
могла быть использована многократно, описание классов следует
выполнять в специальном проекте (шаблон класса), выполнить
компиляцию проекта, а затем делать ссылки (Reference) на файл с
библиотекой из текущих проектов.
1.
2.
3.
4.
5.
6.
7.
Вопросы для самопроверки
Что такое «событие»? Под событием принято понимать действие
или сообщение о нем?
Как мы объявляем (publish) события в собственном классе?
Как мы регистрируем (subscribe) получателя события?
Как мы генерируем (raise) события?
Что представляет собой архитектура событий в .NET Framework?
Происходит ли автоматическое добавление созданной вами
библиотека классов в библиотеку базовых классов .NET
Framework?
При возникновении ошибки (исключения) программа проверяет
все блоки Catch и двигается вниз до тех пор, пока не будет
найден подходящий блок. Почему принято обработчики
конкретных ошибок размещать в первых блоках Catch, а
обработчики общих ошибок – в конце списка?
Литература
1. Чакраборти А., Кранти Ю., Сандху Р.Дж. Microsoft .NET
Framework: разработка профессиональных проектов: Пер. с англ. —
СПб.: БХВ-Петербург, 2005. – 896 с.: ил.
2. Ольсен Э., Эллисон д., Спир Дж. Visual Basic .NET. Разработка
классов. Справочник/Практ. Пособ./Пер. с англ. — М. Издательство
«СП ЭКОМ», 2006. – 416 с. ил.
МЕЖДУНАРОДНЫЙ БАНКОВСКИЙ ИНСТИТУТ
INTERNATIONAL BANKING INSTITUTE
3. Троелсен Э. C# и платформа .NET. Библиотека программиста —
СПб.: Питер, 2006 — 796 с.: ил.
4. Visual Basic.NET: учебный курс/ В.Долженков, М.Мозговой. —
СПб.: Питер, 2005. – 465 с,: ил.
Download