- •Лекция 20, 21. Делегаты и события
- •Пример делегата для обычной функции
- •Пример делегата для метода класса
- •1.Делегат для выбора методов на этапе выполнения
- •Delegate.Invoke или что там внутри? (для продвинутых)
- •MulticastDelegate.GetInvocationList
- •Пример делегата для работы с несколькими методами
- •2. Делегат как оповещатель. Паттерн «наблюдатель»
- •Пример делегата для оповещения наблюдателей
- •3. Делегат как процедура обратного вызова
- •Пример передачи делегата в качестве параметра функции
- •4.Делегат, как обработчик событий
- •[Cпецификаторы] event тип делегата имя события, где
- •Пример делегата для оповещения наблюдателей с помощью событий
- •Стандартные делегаты
- •Пример стандартного делегата EventHandler для оповещения наблюдателей с помощью событий
1.Делегат для выбора методов на этапе выполнения
Делегаты используются для получения возможности определять вызываемый метод не при компиляции, а во время выполнения программы (динамически). То есть делегат может связываться не с одним методом, а с несколькими (экземпляры делегата могут содержать несколько ссылок на методы).
Благодаря этому, можно подсоединять к одному делегату несколько методов, каждый из которых при единственном обращении к делегату будет вызваться по цепочке. Таким образом, из программы будет виден лишь один делегат, за которым скрывается несколько методов (рисунок).

Delegate.Invoke или что там внутри? (для продвинутых)
Метод Invoke недокументирован. Он не является членом классов Delegate и MulticastDelegate. Соответственно, можно предположить, что это специальный метод, генерируемый компилятором. Его изучение показывает, что метод лишен какого-либо кода. Он попросту пустой и ничего не может делать. Так может показаться, если не принимать во внимание спецификатор runtime, используемый при определении метода. Данный спецификатор указывает на то, что метод будет реализован по ходу исполнения программы самой средой исполнения. Это означает, что код, обрабатывающий вызов метода, располагается в недрах самой среды исполнения. А она прекрасно осведомлена о внутреннем устройстве делегата и справится с вызовом всех его методов без дополнительной помощи со стороны программиста.
Код, реализующий делегат, также находится внутри среды исполнения. Оказывается, метод Invoke отвечает не только за вызов делегата, но также за хранение информации о прототипе методов, на которые может ссылаться делегат. Прототип закодирован в самом методе Invoke, то есть его прототип полностью совпадает с прототипом делегата.
MulticastDelegate.GetInvocationList
Возвращает список делегатов, находящихся в списке вызовов делегата.
|
public sealed override Delegate[] GetInvocationList(); |
Существует два недостатка механизма вызова функций, связанных с делегатом. И оба они связаны со случаем, когда в списке вызова делегата присутствует более одной функции.
Функции, на которые ссылаются делегаты, могут возвращать значения. Но мы их получить не сможем, поскольку штатная функция вызова Invoke возвращает значение, которая вернула последняя из опрошенных функций.
При обращении к делегату, содержащему в списке вызова несколько функций, вовсе не гарантируется, что все функции из списка будут вызваны. Если одна из них сгенерирует исключение, то работа метода Invoke будет прервана и остальные функции вызваны не будут.
Пример делегата для работы с несколькими методами
namespace ConsoleApplication1
{
class Program
{
//объявление делегата
delegate void MyDelegate(string s);
// Первый метод, на который мы будем ссылаться при помощи
// делегата. Именно он будет вызывать исключение,
// не позволяющее обратиться ко второму методу.
static void f1(string s)
{
// Выведем значение переданного параметра,
// а также уведомим пользователя о том, что данный
// метод был вызван.
Console.WriteLine("Функция 1 вызвана с параметром = {0}",s);
// Преднамеренно выбросим исключение.
throw new Exception();
}
// Второй метод, на который мы будем ссылаться из делегата.
static void f2(string s)
{
// Сообщим пользователю о том, что метод
// был вызван, а также выведем значение переданного параметра.
Console.WriteLine("Функция 2 вызывана с параметром = {0}",s);
}
static void Main(string[] args)
{
MyDelegate del = new MyDelegate(f1);
// Присоединим к нему еще одну функцию.
del += new MyDelegate(f2);
// Последовательно пройдем по каждому делегату, входящему
// в список вызова ранее созданного делегата.
foreach (MyDelegate d in del.GetInvocationList())
{
// вызов функции - в защищенный блок!
try
{
d("Hello");
}
// Это блок обработки исключений, произошедших
// в защищенном блоке.
catch(Exception ex)
{
// Сообщим пользователю о том, что при попытке
// вызова одной из функций произошло исключение.
Console.WriteLine("Oh mama, была обнаружена исключительная ситуация!");
}
}
Console.ReadKey(); } }}
