TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #16 1.57K
День одиннадцатый. #ЗаметкиНаПолях
События.
С самого начала работы с формами ещё на 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. В основной программе:
//создаём объект наблюдения
MailManager mailMgr = new MailManager();
…
//создаём наблюдателя и подписываем его
Fax fax = new Fax(mailMgr);
fax.Register();
…
//возникновение события
mailMgr.SimulateNewMail(…);

Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 11.
  • 👍 5
More from @netdeveloperdiary
  1. Sep 30, 2026Post #3356
  2. Sep 29, 2026Фото 3 (с) Анатолий Кулаков
  3. Sep 29, 2026День 2799. Конференция DotNext 2026. Часть 1 25 и 26 сентября в Москве прошла очередная ко…
  4. Sep 28, 2026День 2798. #Оффтоп Утиная Типизация в C# с Помощью Перехватчиков. Часть 2 Некоторое время…
  5. Sep 27, 2026День 2797. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Окончание Начало Продол…
  6. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →