Делегаты. Назначение делегатов
В консольных приложения функция Main(), тем или иным способом направляет запросы соответствующим объектам. В Windows-приложениях должно существовать обратное обращение объектов к вызывающей стороне. Ведь современные приложения выполняются нелинейно, так как Windows- приложения основаны на обработке событий. В Интернет-приложениях нужно ожидать сетевого запроса.
Одно из полезных нововведений в С# — делегаты (delegates). Их назначение, по сути, совпадает с указателями функций в C++, но делегаты являются управляемыми объектами и привязаны к типам. Это значит, что исполняющая среда (runtime) гарантирует, что делегат указывает на допустимый объект, а это в свою очередь означает получение всех достоинств указателей функций без связанных с этим опасностей, таких как применение недопустимых адресов или разрушение памяти других объектов.
Мы узнали, как определяются и реализуются интерфейсы. Как вы знаете, с концептуальной точки зрения интерфейсы — это связки между двумя различными частями кода. Но при этом интерфейсы во многом напоминают классы, так как объявляются в период компиляции и могут включать методы, свойства, индексаторы и события. Что касается делегата, то он ссылается на метод и определяется в период выполнения.
В С# две основных области применения делегатов: методы обратного вызова и обработчики событий.
Использование делегатов в качестве методов обратного вызова. Методы обратного вызова повсеместно используются в ОС Windows для передачи указателя функции другой функции, чтобы последняя могла вызвать первую (через переданный ей указатель). Так, функция Win32API EnumWindowsперечисляет все окна верхнего уровня на экране и для каждого окна вызывает переданную ей функцию. Обратные вызовы применяют по-разному, но наиболее распространены два случая:
- асинхронная обработка;методы обратного вызова используют при асинхронной обработке, когда вызванному коду требуется существенное время для обработки запроса. Обычно сценарий таков. Клиентский код вызывает метод, передавая ему метод обратного вызова. Вызванный метод начинает работу в своем потоке и сразу возвращает управление. Запущенный поток затем выполняет основную работу, при необходимости обращаясь к функции обратного вызова. Очевидное достоинство такого подхода в том, что клиент продолжает работу, не блокируясь на потенциально длительное время, которое требуется для синхронного вызова.
- введение дополнительного кода в код класса;другой распространенный способ применения методов обратного вызова имеет место, когда класс позволяет клиенту указать метод для дополнительной нестандартной обработки. Например, в классе списка Windows.Listboxможно указать нисходящий или восходящий порядок сортировки элементов. Кроме еще некоторых базовых возможностей для сортировки, этот класс на самом деле не дает полной свободы действий и остается общим классом. Но при этом Listboxпозволяет указывать для сортировки функцию обратного вызова. Таким образом, Listbox для сортировки вызывает функцию обратного вызова, и ваш код может выполнять нужные нестандартные действия.
Применять интерфейсы в качестве механизма обратного вызова трудоемко. Поэтому в NET.Framework разработан другой механизм – события и делегаты.
Событие (event) – это отправленное объектом уведомление о совершении какого-либо действия. Действие может быть выполнено пользователем или приложением. Объект, сгенерировавший событие, называется отправителем. Объект, перехвативший событие и реагирующий на него, называется получателем события. При обработке событий объект не знает, какой объект обработается, поэтому нужен посредник между источником и получателем события. Для этого в NET.Framework и используется специальный тип Delegate.
Итак, делегат – это класс, содержащий ссылку на метод, или, возможно несколько методов.
Объявление стандартного делегата события:
public delegate void EventHandler( object sender, EventArgs e);
У делегата тип возврата – void , первый параметр – объект, который ссылается на источник события, второй параметр содержит данные о событии (например, какая кнопка мыши нажата).
Итак, тип делегата хранит три элемента информации:
¾ имя метода, к которому должен обращаться вызов;
¾ аргументы метода;
¾ возвращаемое методом значение.
После создания делегата и получения такой информации делегат может динамически, во время работы приложения, вызывать методы, на которые он указывает.
Примечания:
1. В делегатах прописывается ссылка на метод, то есть адрес метода.
2. Делегат должен вызывать только такие методы, у которых тип возвращаемого значения и список параметров совпадает с соответствующими элементами делегатов.
Многоадресатная передача (мультикастинг).Одно и тоже событие, например, завершение работы приложения, может выполняться несколькими способами. Для каждого способа должен быть разработан метод. В таких случаях нужно разработать делегат, который будет способен создавать список вызовов методов. Для этого сначала создают объект делегат, а затем добавляют в него ссылки на методы с помощью оператора "плюс". Исполняющая среда распознает, что эти методы нужно вызвать последовательно. Кроме того, можно убрать любой делегат из составного оператором "минус".
Итак, делегаты — привязанные к типу управляемые объекты — играют в С# ту же роль, что и указатели функций в C++. Отличие делегатов от классов и интерфейсов в том, что они ссылаются на один метод и определяются в период выполнения. Делегаты широко применяются для асинхронной обработки и добавления нестандартного кода к коду классов. Делегаты могут использоваться для многих целей, включая методы обратного вызова, определение статических методов и обработку событий.