События.
С самого начала работы с формами ещё на Delphi, мне всегда было интересно, что это за магические параметры у вызова события нажатия на кнопку:
OnClick(Object sender, EventArgs e);На самом деле это реализация паттерна проектирования «Наблюдатель». Если коротко, то у нас есть объект наблюдения (допустим система сообщений), и есть «наблюдатели», например, различные сервисы отправки сообщений: факс, смс, e-mail и т.п.
Паттерн состоит в следующем:
- наблюдатели подписываются на рассылку, передавая объекту делегат метода обратного вызова
- объект при возникновении нового события вызывает этот метод у всех подписанных наблюдателей, передавая в него «сообщение» - класс, наследуемый от
EventArgs (e) и ссылку на себя (sender).Рассмотрим, реализацию этого паттерна на С#:
1. Определяем тип сообщения, например, письмо:
class NewMailEventArgs : EventArgs {
public string From {get; set;}
public string To {get; set;}
public string Message {get; set;}
}
2. В объекте наблюдения определяем член-событие:class MailManager {
public event EventHandler<NewMailEventArgs> NewMail;
…
}
Здесь обобщённый делегат System.EventHandler принимает тип нашего сообщения. Значит наблюдатели должны предоставить метод обратного вызова, прототип которого совпадает с делегатом. Например:void Method(Object sender, NewMailEventArgs e);Объект наблюдения ответственен за создание объекта-сообщения и вызов метода обратного вызова с передачей ему ссылки на себя (
Object sender) и сообщения (NewMailEventArgs e).3. В объекте наблюдения определяем метод, уведомляющий о событии:
class MailManager {
…
//уведомляем о событии
private void OnNewMail(NewMailEventArgs e) {
EventHandler<NewMailEventArgs> temp = NewMail;
if(temp != null) temp(this, e);
}
…
}
4. В объекте наблюдения определяем метод, вызывающий событие:class MailManager {
…
public void SimulateNewMail(…) {
…
//создаём сообщение
var msg = new NewMailEventArgs{From=“…”, To=“…”, Message=“…”};
//вызываем метод, уведомляющий о событии
OnNewMail(msg);
}
}
5. Определяем наблюдателя:class Fax {
//храним ссылку на объект наблюдения
private MailManager mailMgr;
//передаём в конструктор ссылку на объект наблюдения
public Fax(MailManager mm) {
mailMgr = mm;
}
// метод обратного вызова
private void FaxMsg(Object sender, NewMailEventArgs e) {
…
// обрабатываем сообщение e (отправляем факс)
…
}
// подписываемся на уведомления (можно сразу в конструкторе)
public void Register() {
mailMgr.NewMail += FaxMsg;
}
// отписываемся от уведомлений
public void Unregister() {
mailMgr.NewMail -= FaxMsg;
}
}
C# допускает упрощённый синтаксис подписки и отпискиmailMgr.NewMail += FaxMsg;На самом деле в объекте наблюдения хранится список наблюдателей. Оператор += вызывает метод, добавляющий наблюдателя в список, а -=, соответственно, метод, удаляющий из списка.
mailMgr.NewMail -= FaxMsg;
6. В основной программе:
//создаём объект наблюденияИсточник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 11.
MailManager mailMgr = new MailManager();
…
//создаём наблюдателя и подписываем его
Fax fax = new Fax(mailMgr);
fax.Register();
…
//возникновение события
mailMgr.SimulateNewMail(…);