Событие (объектно-ориентированное программирование)
Событие в объектно-ориентированное программировании — это сообщение, которое возникает в различных точках исполняемого кода при выполнении определённых условий.
События предназначены для того, чтобы иметь возможность предусмотреть реакцию программного обеспечения. [1]
Для решения поставленной задачи создаются обработчики событий: как только программа попадает в заданное состояние, происходит событие, посылается сообщение, а обработчик перехватывает это сообщение. В общем случае в обработчик не передаётся ничего, либо передаётся ссылка на объект, инициировавший (породивший) обрабатываемое событие. В особых случаях в обработчик передаются значения некоторых переменных или ссылки на какие-то другие объекты, чтобы обработка данного события могла учесть контекст возникновения события.
Самое простое событие — это событие, сообщающее о начале или о завершении некоторой процедуры. Событие, по сути, сообщает об изменении состояния некоторого объекта. Наиболее наглядно события представлены в пользовательском интерфейсе, когда каждое действие пользователя порождает цепочку событий, которые, затем обрабатываются в приложении.
Общее описание
В объектно-ориентированном анализе для описания динамического поведения объектов принято использовать модель состояний. [2]
Событие — это переход объекта из одного состояния в другое. Взаимодействие объектов также осуществляется при помощи событий: изменение состояния одного объекта приводит к изменению состояния другого объекта, а событие оказывается средством связи между объектами.
Согласно [2] , событие — это «абстракция инцидента или сигнала в реальном мире, который сообщает нам о перемещении чего- либо в новое состояние». Далее, выделяются четыре аспекта события:
- метка — уникальный идентификатор события.
- значение — текстовое сообщение о сути произошедшего.
- предназначение — модель событий, которая принимает событие.
- данные — данные, которые переносится от одного объекта к другому.
Примеры
Первый ряд примеров событий доставляет собственно сам жизненный цикл объекта:
- создание объекта;
- уничтожение объекта.
Более сложные примеры событий возникают тогда, когда у объекта появляются внутренние состояния, которые описываются соответствующей диаграммой переходов (из одного состояния в другое).
Пример на VB.NET
События позволяют классу или объекту уведомлять другие классы или объекты о возникновении каких-либо ситуаций. Класс, отправляющий (или вызывающий) событие, называется издателем, а классы, принимающие (или обрабатывающие) событие, называются подписчиками. В VB.NET события объявляются ключевым словом Event. Если опущен тип делегата, то компилятор сам создаст его, который может в дальнейшем содержать ссылку на метод реализованный в подписчике.
Реализовать подписку на событие можно несколькими способами:
- evtSample As sampleDel — механизм регистрации обработчика события для данного типа декларации должен быть предоставлен классом с объявленым событием. Событие генерируется путем вызова метода в делегате evtSample.
- Public Event evtSample As sampleDel — обработчик событий может быть зарегистрирован с помощью оператора AddHandler, который свяжет событие источника и метод в классе подписчика. Событие реализуется с помощью объявленного делегата sampleDel. Событие генерируется используя оператор RaiseEvent.
- Public Event evtSample — обработчик события будет зарегистрирован с помощью ключевых слов WithEvents в объявлении экземпляра класса и Handles в самой декларации метода класса подписчика. Событие реализуется с помощью неявно объявленного делегата. Событие генерируется используя оператор RaiseEvent.
Imports System Public Class CTimer Delegate Sub SecondDel(ByVal xintTime As Integer) Private evtSecond As SecondDel Public Event evtMinute As SecondDel Public Event evtHour(ByVal xHour As Integer) public Shared lngSeconds As Long Public Sub Register(ByVal objSecond As SecondDel) evtSecond = evtSecond.Combine(evtSecond, objSecond) End Sub Public Sub OnTimer() lngSeconds = lngSeconds + 1 If lngSeconds Mod 5 = 0 Then evtSecond(lngSeconds) 'Вызов метода делегата End If If lngSeconds Mod 10 = 0 Then RaiseEvent evtMinute(lngSeconds) 'Генерация события End If If lngSeconds Mod 30 = 0 Then RaiseEvent evtHour(lngSeconds) 'Генерация события End If End Sub End Class Public Class CClock Private WithEvents mobjTimer As CTimer 'Объявление объекта класса, с возможностью подключения к событиям Sub New() mobjTimer = New CTimer() mobjTimer.Register(New CTimer.SecondDel(AddressOf SecondEvent)) 'Регистрация события через метод предоставленный классом AddHandler mobjTimer.evtMinute, AddressOf MinuteEvent 'Регистрация события с помощью оператора AddHandler While (mobjTimer.lngSeconds 60) mobjTimer.OnTimer() System.Threading.Thread.Sleep(100) End While End Sub Private Sub SecondEvent(ByVal xintTime As Integer) Console.WriteLine("Second's Event") End Sub Private Sub MinuteEvent(ByVal xintTime As Integer) Console.WriteLine("Minute's Event") End Sub 'Регистрация события с помощью ключевого слова Handles Private Sub mobjTimer_evtHour(ByVal xintTime As Integer) Handles mobjTimer.evtHour Console.WriteLine("Hour's Event") End Sub Public Shared Sub Main() Dim cc1 = New CClock() End Sub End Class
См. также
Ссылки
- Визуальное объектно-ориентированное программирование
- Введение в объектно-ориентированное программирование
- Основы объектно-ориентированного визуального программирования
- Article Event Handling in VB.NET
- Events and Event Handlers in VB.NET
- event keyword in C#
- Events and Delegates .NET Framework
Примечания
Литература
- Шлеер С, Меллор С. Объектно-ориентированный анализ: моделирование мира в состояниях: Пер. с англ. — Киев : Диалектика, 1993. — 240 с: ил.
Учебники. Программирование для начинающих.
Programm.ws — это сайт, на котором вы можете почитать литературу по языкам программирования , а так-же посмотреть примеры работающих программ на С++, ассемблере, паскале и много другого..
Программирование — в обычном понимании, это процесс создания компьютерных программ.
В узком смысле (так называемое кодирование) под программированием понимается написание инструкций — программ — на конкретном языке программирования (часто по уже имеющемуся алгоритму — плану, методу решения поставленной задачи). Соответственно, люди, которые этим занимаются, называются программистами (на профессиональном жаргоне — кодерами), а те, кто разрабатывает алгоритмы — алгоритмистами, специалистами предметной области, математиками.
В более широком смысле под программированием понимают весь спектр деятельности, связанный с созданием и поддержанием в рабочем состоянии программ — программного обеспечения ЭВМ. Более точен современный термин — «программная инженерия» (также иначе «инженерия ПО»). Сюда входят анализ и постановка задачи, проектирование программы, построение алгоритмов, разработка структур данных, написание текстов программ, отладка и тестирование программы (испытания программы), документирование, настройка (конфигурирование), доработка и сопровождение.
Delphi для профессионалов
Глава 1 Объектно-ориентированное программирование
События
Программистам, давно работающим в Windows, наверное, не нужно пояснять смысл термина «событие». Сама эта среда и написанные для нее программы управляются событиями, возникающими в результате воздействий пользователя, аппаратуры компьютера или других программ. Весточка о наступлении события — это сообщение Windows, полученное так называемой функцией окна. Таких сообщений сотни, и, по большому счету, написать программу для Windows — значит определить и описать реакцию на некоторые из них.
Работать с таким количеством сообщений, даже имея под рукой справочник, нелегко. Поэтому одним из больших преимуществ Delphi является то, что программист избавлен от необходимости работать с сообщениями Windows (хотя такая возможность у него есть). Типовых событий в Delphi — не более двух десятков, и все они имеют простую интерпретацию, не требующую глубоких знаний среды.
Рассмотрим, как реализованы события на уровне языка Object Pascal. Событие — это свойство процедурного типа, предназначенное для создания пользовательской реакции на то или иное входное воздействие:
property OnMyEvent: TMyEvent read FOnMyEvent write FOnMyEvent;
Здесь FOnMyEvent — поле процедурного типа, содержащее адрес некоторого метода. Присвоить такому свойству значение — значит указать объекту адрес метода, который будет вызываться в момент наступления события. Такие методы называют обработчиками событий. Например, запись:
означает, что при активизации объекта Application (так называется объект, соответствующий работающему приложению) будет вызван метод-обработчик MyActivatingMethod .
Внутри библиотеки времени выполнения Delphi вызовы обработчиков событий находятся в методах, обрабатывающих сообщения Windows. Выполнив необходимые действия, этот метод проверяет, известен ли адрес обработчика, и, если это так, вызывает его:
if Assigned(FOnMyEvent) then FOnMyEvent(Self);
События имеют разное количество и тип параметров в зависимости от происхождения и предназначения. Общим для всех является параметр sender — он указывает на объект-источник события. Самый простой тип — TNotifyEvent — не имеет других параметров:
TNotifyEvent = procedure (Sender: TObject) of object;
Тип метода, предназначенный для извещения о нажатии клавиши, предусматривает передачу программисту кода этой клавиши о передвижении мыши — ее текущих координат и т. п. Все события в Delphi принято предварять префиксом On: onCreate, onMouseMove, onPaint и т. д. Дважды щелкнув в Инспекторе объектов на странице Events в поле любого события, вы получите в программе заготовку метода нужного типа. При этом его имя будет состоять из имени текущего компонента и имени события (без префикса On), а относиться он будет к текущей форме. Пусть, например, на форме Forml есть текст Label1 . Тогда для обработки щелчка мышью (событие O nclick ) будет создана заготовка метода TFormi.Label1click:
procedure TForml.LabellClick(Sender: TObject);
begin
end;
Поскольку события — это свойства объекта, их значения можно изменять в любой момент во время выполнения программы. Эта замечательная возможность называется делегированием. Можно в любой момент взять способы реакции на события у одного объекта и присвоить (делегировать) их другому:
Принцип делегирования позволяет избежать трудоемкого процесса порождения новых дочерних классов для каждого специфического случая, заменяя его простой подстановкой процедур. При необходимости можно выбирать один из нескольких возможных вариантов обработчиков событий.
Но какой механизм позволяет подменять обработчики, ведь это не просто процедуры, а методы? Здесь как нельзя кстати приходится существующее в Object Pascal понятие указателя на метод. Отличие метода от процедуры состоит в том, что помимо явно описанных параметров методу всегда неявно передается еще и указатель на вызвавший его экземпляр класса (переменная self ). Вы можете описать процедурный тип, который будет совместим по присваиванию с методом (т. е. предусматривать получение self ). Для этого в описание процедуры нужно добавить зарезервированные слова of object . Указатель на метод — это указатель на такую процедуру.
type
TMyEvent = procedure(Sender: TObject; var AValue: Integer) of object;
TlstObject = class;
FOnMyEvent: TMyEvent;
property OnMyEvent: TMyEvent read FOnMyEvent write FOnMyEvent;
end;
T2ndObject = class;
procedure SetValuel(Sender: TObject; var AValue: Integer);
procedure SetValue2(Sender: TObject; var AValue: Integer);
end;
.
var
Objl: TlstObject;
Obj2: T2ndObject;
begin
Objl := TlstObject.Create;
Obj2 := T2ndObject.Create;
Obj1.OnMyEvent := Obj2.SetValuel;
Obj1.OnMyEvent := Obj2.SetValue2;
.
end.
Этот пример показывает, что при делегировании можно присваивать методы других классов. Здесь обработчиком события OnMyEvent объекта Objl по очереди выступают методы SetValuel и Setvaiue2 объекта Obj2 .
Обработчики событий нельзя сделать просто процедурами — они обязательно должны быть чьими-то методами. Но их можно «отдать» какому-либо другому объекту. Более того, для этих целей можно описать и создать специальный объект. Его единственное предназначение — быть носителем методов, которые затем делегируются другим объектам. Разумеется, такой объект надо не забыть создать до использования его методов, а в конце — уничтожить. Можно и не делать этого, объявив методы методами класса, о которых речь пойдет в одном из последующих разделов.
Мы сейчас решили задачу использования нескольких разных обработчиков того или иного события для одного объекта. Но не менее часто требуется решить обратную задачу — а как использовать для различных событий разных объектов один и тот же обработчик?
Если никакой «персонификации» объекта, вызвавшего метод, не нужно, все делается тривиально и проблемы не возникает. Самый простой пример: в современных программах основные функции дублируются дважды — в меню и на панели инструментов. Естественно, сначала нужно создать и наполнить метод содержимым (скажем, для пункта меню), а затем в Инспекторе объектов указать его же для кнопки панели инструментов.
Более сложный случай, когда внутри такого метода нужно разобраться, кто собственно его вызвал. Если потенциальные кандидаты имеют разный объектный тип (как в предыдущем абзаце — кнопка и пункт меню), то именно объектный тип можно применить в качестве критерия:
If Sender is TMenuItem then ShowMessage(‘Выбран пункт меню’);
Если же все объекты, разделяющие между собой один обработчик события, относятся к одному классу, то приходится прибегать к дополнительным ухищрениям. Типовой прием — использовать свойство Tag , которое имеется у всех компонентов, и, вполне вероятно, именно для этого и задумывалось:
const colors : array[0..7]
of
TColor = clWhite,clRed,clBlue,clYellow,clAqua,clGreen, clMaroon,clBlack);
procedure TForml.CheckBoxClick(Sender: TObject);
begin
with TCheckBox(Sender) do
if Checked
then Color := Colors[Tag]
else Color := clBtnFace;
end;
Пусть в форме имеется несколько переключателей. Для того чтобы при нажатии каждый из них окрашивался в свой цвет, нужно в Инспекторе объектов присвоить свойству Tag значения от 0 до 7 и для каждого связать событие onclick с методом CheckBoxClick . Этот единственный метод справится с задачей для всех переключателей.
События C# по-человечески
Невозможно, просто взять и вникнуть в этот глубокий смысл, изучая События (event) в просторах базового и, на первый взгляд, бесконечного C#.
Когда я изучал События (не в рамках .NET!), потратил много сил, чтобы, наконец-то, разобраться, как они устроены и должны конструироваться. Поэтому, я решил опубликовать свою методику понимания структуры пользовательского события, коим представляется ключевое слово event в С#.
Не буду цитировать и без того замученную MSDN, а постараюсь объяснить понятно и доступно.
- 1. Вы не должны испытывать страх перед изучением. Пожалуйста, читайте медленно и вдумчиво.
- 2. Вы должны понимать классы и методы.
- 3. Вы должны
знатьпонимать, что есть делегаты. Хотя, Вы можете попытаться понять их в ходе чтения статьи.
Итак, Событие, это ситуация, при возникновении которой, произойдут некоторые действия. Само событие имеет определенную структуру.
Предположим, что стоит такая задача: определено три класса. Первый класс будет считать до 100, используя цикл. Два других класса будут ждать, когда в первом классе счетчик досчитает, например, до 71, и после этого каждый выведет в консоль фразу «Пора действовать, ведь уже 71!». Проще говоря, при обнаружении значения 71, вызовутся по методу, соответственно для каждого класса. Разложим все по полкам.
1. Моделирование ситуации.
Подготовим эти три простейших класса, оставив точку входа в программу main нетронутой.
Класс ClassCounter и его метод Count() в котором будет производится счет. (В коде я опускаю пространства имен namespace, ибо это ясно, как день).
class ClassCounter //Это класс - в котором производится счет. < public void Count() < //Здесь будет производиться счет >>
Два других класса (имена им Handler_I и Handler_II), которые должны реагировать на возникновение события методами public void Message(). У каждого по методу, как и договаривались.
class Handler_I //Это класс, реагирующий на событие (счет равен 71) записью строки в консоли. < public void Message() < //Не забудьте using System //для вывода в консольном приложении Console.WriteLine("Пора действовать, ведь уже 71!"); >>
class Handler_II < public void Message() < Console.WriteLine("Точно, уже 71!"); >>
Напомню, когда счетчик будет считать до 100 и достигнет 71, должны сработать методы Message() для классов Handler_I и Handler_II.
Теперь вернемся к классу ClassCounter и создадим счетчик при помощи цикла for c переменной-счетчиком int i.
class ClassCounter //Это класс - в котором производится счет. < public void Count() < for (int i = 0; i < 100; i++) < >> >
Первый этап завершен. У нас есть класс счетчик и два класса, которые будут выводить сообщения. Условия задачи: как только i=71, должны сработать методы Message() для двух классов Handler_I и Handler_II.
2. Оформление события.
Абстрагируемся от программирования. Событие, которое мы хотим создать, будет представлять фразу «… счетчик считает. И как только он будет равен 71, должны выполниться действия». Значит, нам необходимо условие «как только он будет равен 71». Представим его при помощи условного оператора if.
class ClassCounter //Это класс - в котором производится счет. < public void Count() < for (int i = 0; i < 100; i++) < if (i == 71) < >> > >
Конструируем событие event. Определяем по методам, которые должны сработать при i=71 их сигнатуру (или прототип).
Сигнатура метода — это так называемая спецификация (или простыми словами «шаблон») какого-л. метода или методов. Представляет собой сочетание названия типа, который метод возвращает, плюс название типов входящих параметров (по порядку! порядок очень важен.)
Например, метод int NewMethod(int x, char y) будет иметь сигнатуру int (int, char), а метод void NewMethod() — void (void).
Как толкует MSDN, события (event) основаны на делегатах (delegate), а делегат, говоря очень простым языком — «переменная, хранящая ссылку на метод». Как Вы уже поняли, т.к. наше событие будет ссылаться на два метода void Message(), мы должны определить сигнатуру этих методов, и составить на основе этой сигнатуры делегат. Сигнатура выглядит так: void (void).
Определяем делегат (назовем его MethodContainer):
class ClassCounter //Это класс - в котором производится счет. < //Синтаксис по сигнатуре метода, на который мы создаем делегат: //delegate ИмяДелегата(); //Мы создаем на void Message(). Он должен запуститься, когда условие выполнится. public delegate void MethodContainer(); public void Count() < for (int i = 0; i < 100; i++) < if (i == 71) < >> > >
Далее, мы создаем событие при помощи ключевого слова event и связываем его с этим делегатом (MethodContainer), а, следовательно, c методами, имеющими сигнатуру void (void). Событие должно быть public, т.к. его должны использовать разные классы, которым нужно как-то отреагировать (классы Handler_I и Handler_II).
Событие имеет синтаксис: public event ;
Название делегата — это имя делегата, на который «ссылаются» наши методы.
class ClassCounter //Это класс - в котором производится счет. < public delegate void MethodContainer(); //Событие OnCount c типом делегата MethodContainer. public event MethodContainer onCount; public void Count() < for (int i = 0; i < 100; i++) < if (i == 71) < >> > >
Теперь запустим наше событие onCount, в условии когда i=71:
if (i == 71)
Все. Событие создано. Методы, которые вызовет это событие, определены по сигнатурам и на основе их создан делегат. Событие, в свою очередь, создано на основе делегата. Пора показать событию onCount, какие же все-таки методы должны сработать (мы ведь указали только их сигнатуру).
3. Подписка.
Вернемся в точку входа программы main и создадим экземпляр класса ClassCounter. А также создадим по экземпляру классов, которые должны запуститься. (Они должны быть public).
class Program < static void Main(string[] args) < ClassCounter Counter = new ClassCounter(); Handler_I Handler1 = new Handler_I(); Handler_II Handler2 = new Handler_II(); >>
Теперь укажем событию onCount, методы, которые должны запуститься.
Происходит это следующим образом: . += . .
Никаких скобочек после метода! Мы же не вызываем его, а просто указываем его название.
class Program < static void Main(string[] args) < ClassCounter Counter = new ClassCounter(); Handler_I Handler1 = new Handler_I(); Handler_II Handler2 = new Handler_II(); //Подписались на событие Counter.onCount += Handler1.Message; Counter.onCount += Handler2.Message; >>
Проверка.
Теперь осталось запустить счетчик класса ClassCounter и подождать, пока i станет равным 71. Как только i=71, запустится событие onCount по делегату MethodContainer, который (в свою очередь) запустит методы Message(), которые были подписаны на событие.
class Program < static void Main(string[] args) < ClassCounter Counter = new ClassCounter(); Handler_I Handler1 = new Handler_I(); Handler_II Handler2 = new Handler_II(); Counter.onCount += Handler1.Message; Counter.onCount += Handler2.Message; //Запустили счетчик Counter.Count(); >>
Результат:
Пора действовать, ведь уже 71!
Точно, уже 71!
Заключение.
- 1. Определите условие возникновения события и методы которые должны сработать.
- 2. Определите сигнатуру этих методов и создайте делегат на основе этой сигнатуры.
- 3. Создайте общедоступное событие на основе этого делегата и вызовите, когда условие сработает.
- 4. Обязательно (где-угодно) подпишитесь на это событие теми методами, которые должны сработать и сигнатуры которых подходят к делегату.
Преимущество Событий очевидно: классу-издателю, генерирующему событие не нужно знать, сколько классов-подписчиков подпишется или отпишется. Он создал событие для определенных методов, ограничив их делегатом по определенной сигнатуре.
События широко используются для составления собственных компонентов управления (кнопок, панелей, и т.д.).
У самых маленьких может возникнуть вопрос: что делать, если методы, которые должны сработать имеют входящий параметр (а то и не один!)?
Ответ: Все дело в делегате, на котором базируется событие. А точнее сигнатура подходящих для делегата методов. Когда Вы сконструируете делегат, «принимающий» метод с параметром, то (!) при запуске событие запросит этот параметр. Естественно, параметр может быть чем угодно.
Пару слов о .NET-событиях. Microsoft упростила задачу конструирования делегатов: .NET предлагает готовый делегат EventHandler и т.н. «пакет» входных параметров EventArgs. Желаете событие? Берете готовый EventHandler, определяетесь в параметрах, «запихиваете» их в класс, а класс наследуете от EventArgs. А дальше — как по расписанию)
Многие разработчики утверждают (и я с ними согласен), что главная проблема «недопонимания» событий — их специфическая область применения, а вследствие — мало доступных примеров. Ну и не забывайте о практике.
P.S. Если вы не ни разу не использовали делегаты, лучше попробуйте потренироваться на делегатах, а затем попытайтесь понять эту статью.
Я надеюсь, что внес небольшое понимание в эту непростую тему. Успехов!
Событие (event) — что это в программировании
Событие — достижение какое-то состояние программной системой и/или вызов в ней специальной функции наступление этого события обозначающей.
Реализация механизма событий
- В разных системах прикладному коду часто даётся возможность обработать событие (запустить обработчик).
- Наступление события может по своей сути быть просто достижением места в программе (коде), где начинается просмотр зарегистрированных обработчиков и их запуск.