Новые средства языка. Делегат на основе обычного метода класса. Делегаты на основе класса MulticastDelegate, страница 4

Важным моментом, который следует запомнить, является то, что к делегатам применим тот же качественный описатель — immutable (неизменяемый), который применяется для объектов класса string. Он означает, что все операции с объектом (строкой текста или делегатом) не способны его изменить. Объект остается таким, каким он был в момент создания. Однако операции, производимые над объектом, возвращают модифицированный объект, который вы можете присвоить новому объекту или тому же самому (и только в этом случае он реально изменится).

Делегаты на основе класса MulticastDelegate

Теперь рассмотрим способность делегатов запускать последовательную цепочку вызовов, например, последовательно вызвать функции Firstname и Surname. Весьма полезно попробовать сделать это самостоятельно с помощью документации. Перед тем, как вы начнете, подскажу, что для обеспечения последовательности или цепочки вызовов используется класс, который является производным от класса Delegate. Он определен как:

public abstract class MulticastDelegate : Delegate

В качестве второй подсказки используйте тот факт, что такие делегаты умеют аккумулировать ссылки, помещая их в свой внутренний динамический связанный список. Для них определены операции + и –, которые вставляют и удаляют из списка ссылки на вызываемые функции. Кроме указанных ссылок, делегат имеет специальный список (invocation list), определяющий очередность запуска функций. Интересно, что в этом списке ссылки (в действительности, адреса функций) могут повторяться. В любом случае функции из списка вызываются поочередно и в той последовательности, в которой он был создан.

Ниже приведена новая версия, реализующая описанный алгоритм. Она производит не очень аккуратный вывод, но это не так важно для восприятия идеи. Главным моментом, который иллюстрирует этот фрагмент, является способ создания и техника управления тем перечнем функций, которые делегат должен выполнить. Замените код одной из ветвей переключателя switch.

foreach (Man m in men)

{

d = new ManDelegate (m.Firstname) + new ManDelegate (m.Surname);

  d();

}

При выборе этой ветви в консольное окно будут выведены по две строки текста (имя и фамилия) для каждого объекта, хранимого в списке men.

Делегаты такого типа (multicast) являются базисом, на котором построено еще одно средство языка С# — события (events). Идея суммирования ссылок, а точнее — коллекционирования их во внутренне поддерживаемом списке (список типа TO DO:), используется при реализации обработки событий. С помощью этого механизма классы могут подписаться на получение оповещений о событиях, происходящих в системе (напоминает подписку на газеты). И это происходит точно таким же образом, то есть добавлением ссылок во внутренний список функций-обработчиков. Мы рассмотрим эту технологию позже, а сейчас отметим, что идея суммирования обработчиков событий весьма эффективно работает в оконных приложениях. Например, вы можете подписать элемент управления, расположенный на форме, на оповещение его о всех тех событиях, которые, как вы считаете, имеют к нему отношение.

Для того, чтобы убедиться в возможности повторять ссылки в том списке действий, которые должен выполнить делегат, вы можете во фрагмент, приведенный выше, добавить такую строку:

//=== Добавляем еще один «пункт» в список заданий делегата

d += new ManDelegate(m.Firstname);

Как видите, делегатный класс переопределяет операцию += для того, чтобы нам с вами было удобно добавлять ссылки в список методов, вызываемых делегатом. Место вставки последней строки вы без труда вычислите сами и, если не ошибетесь, то с помощью делегата d наш фрагмент сделает для каждого объекта по три вызова функций. В консольное окно при этом будет выведена последовательность: имя, фамилия и (вновь) имя объекта.

Заметим, что вызов всех функций из списка, который хранит делегат, можно поизвести также с помощью метода DynamicInvoke класса Delegate. Такой способ выглядит менее изящно, но вы должны знать о его существовании. Замените в последнем фрагменте одну или обе строки d(); на:

d.DynamicInvoke(null);

и запустите программу. Она должна работать так же, как и раньше. Параметр null соответствует тому факту, что сигнатура методов, вызываемых делегатом, не содержит входных параметров. Если задействовать еще несколько методов, унаследованных от делегатных классов, то ветви функции Show можно переписать в еще более замысловатом виде.

case 1:

foreach (Man m in men)