Поиск

Использование делегатов в качестве методов обратного вызова

Методы обратного вызова повсеместно используются в Windows для передачи указателя функции другой функции, чтобы последняя могла вызвать первую (через переданный ей указатель). Так, функция Win32 API
EnumWindows перечисляет все окна верхнего уровня на экране и для каждого окна вызывает переданную ей функцию. Обратные вызовы применяют по-разному, но наиболее распространены два случая.

  • Асинхронная обработка Методы обратного вызова используют при асинхронной обработке, когда вызванному коду требуется существенное время для обработки запроса. Обычно сценарий таков. Клиентский код вызывает метод, передавая ему метод обратного вызова. Вызванный метод начинает работу в своем потоке и сразу возвращает управление. Запущенный поток затем выполняет основную работу, при необходимости обращаясь к функции обратного вызова. Очевидное дбстоинство такого подхода в том, что клиент продолжает работу, не блокируясь на потенциально длительное время, которое требуется для синхронного вызова.
  • Введение дополнительного кода в код класса Другой распространенный способ применения методов обратного вызова имеет место, когда класс позволяет клиенту указать метод для дополнительной нестандартной обработки. Например, в классе Windows Listbox можно указать нисходящий или восходящий порядок сортировки элементов. Кроме еще некоторых базовых возможностей для сортировки, этот класс на самом деле не дает полной свободы действий и остается общим классом. Но при этом Listbox позволяет указывать для сортировки функцию обратного вызова. Таким образом, Listbox для сортировки вызывает функцию обратного вызова, и ваш код может выполнять нужные нестандартные действия.

Рассмотрим пример определения и применения делегата. В этом примере у нас есть класс менеджера базы данных, который отслеживает все активные соединения с БД и предоставляет метод перечисления этих соединений. Если допустить, что менеджер БД находится на удаленной машине, правильным решением будет сделать метод асинхронным, позволив клиенту предоставлять метод обратного вызова. Заметьте: в реальном приложении вам следовало бы задействовать многопоточность, чтобы добиться подлинной асинхронности. Но для упрощения примера и поскольку мы не рассматривали многопоточность, пока не будем ее применять.
Для начала определим два главных класса: DBManager n DBConnection.

class DBConnection <
}
class DBManager { static DBConnection[] activeConnections;
public delegate void EnumConnectionsCallback(DBConnection
connection); public static void EnumConnections(EnumConnectionsCallback
callback) {
foreach (DBConnection connection in activeConnections)
{
callback(connection);
} > }

Метод EnumConnectionsCallback является делегатом, что определяется ключевым словом delegate в начале сигнатуры метода. Как видите, этот делегат возвращает void и принимает единственный аргумент — объект DBConnection. Метод EnumConnections в соответствии с его определением принимает единственный аргумент — метод EnumConnectionsCallback. Чтобы вызвать метод DBManager.EnumConnections нам нужно лишь передать ему экземпляр делегата DBManager.EnumConnectionCallback.
Для создания экземпляра делегата нужно применить new, передав ему имя метода, имеющего ту же сигнатуру, что и у делегата. Вот пример:

DBManager.EnumConnectionsCallback myCallback =
new DBManager.EnumConnectionsCallback(ActiveConnectionsCallback);
DjlManager.EnumConnections(myCallback);
Заметьте, что это можно скомбинировать в единый вызов:
DBManager.EnumConnections(new
DBManager.EnumConnectionsCallback(ActiveConnectionsCallback));

Вот и все, что касается базового синтаксиса делегатов. Теперь посмотрим на законченный пример:

using System;
class DBConnection {
public DBConnection(string name) {
this.name = name; }
protected string Name; public string name {
get {
return this.Маше; }
set {
this.Name = value; > } }
class DBManager {
static DBConnection[] activeConnections;
public void AddConnectionsO
{
activeConnections = new DBConnection[5];
for (int i = 0; i < 5; i++)
{
activeConnections[i] = new
DBConnection("DBConnection " + (i + 1)); > }
public delegate void EnumConnectionsCallback(DBConnection
connection); public static void
EnumConnections(EnumConnectionsCallback
callback) {
foreach (DBConnection connection in activeConnections)
{
callback(connection);
} } >
class DelegatelApp {
public static void
ActiveConnectionsCallback(DBConnection
connection) <
Console.WriteLine("Callback method called for "
+ connection.name); }
public static void Main() {
DBManager dbMgr = new DBManagerO; dbMg
r.AddConnections();
DBManager.EnumConnectionsCallback myCallback
= new DBManager.EnumConnectionsCallback (ActiveConnectionsCallback);
DBManager.EnumConnections(myCallback); } }

После компиляции и запуска этого приложения мы получим такие результаты:

Callback method called for DBConnection 1 Callback method called for
DBConnection 2 Callback method called for DBConnection 3 Callback method
called for DBConnection 4 Callback method called for DBConnection 5