446
Глава.17 .Делегаты
оболочкой
метода
FeedbackToFile
(именно на этого делегата ссылается переменная
fb3
). Наконец, переменной
fbChain
присваивается ссылка на вновь созданного
делегата. При этом ранее созданный делегат и массив, на который ссылается его
поле
_invocationList
, теперь подлежат обработке механизмом уборки мусора.
рис. 17.6.
.Окончательный.вид.цепочки.делегатов
После выполнения кода,
создающего цепочку, переменная
fbChain
передается
методу
Counter
:
Counter(1, 2, fbChain);
Метод
Counter
содержит код неявного вызова метода
Invoke
для делегата
Feedback
. Впрочем, об этом мы уже говорили. Когда метод
Invoke
вызывается для
делегата, ссылающегося на переменную
fbChain
,
этот делегат обнаруживает, что
значение поля
_invocationList
отлично от
null
. Это приводит к выполнению
цикла, перебирающего все элементы массива, вызывая для них метод, оболочкой
которого служит указанный делегат. В нашем примере
методы вызываются в сле-
дующей последовательности:
FeedbackToConsole
,
FeedbackToMsgBox
и, наконец,
FeedbackToFile
.
Реализация метода
Invoke
класса
Feedback
выглядит примерно так (в псевдо-
коде):
447
Обратный.вызов.нескольких.методов.(цепочки.делегатов)
public void Invoke(Int32 value) {
Delegate[] delegateSet = _invocationList as Delegate[];
if (delegateSet != null) {
// Этот массив указывает на делегаты, которые следует вызвать
foreach (Feedback d in delegateSet)
d(value); // Вызов каждого делегата
} else {
// Этот делегат определяет используемый метод обратного вызова.
// Этот метод вызывается для указанного объекта.
_methodPtr.Invoke(_target, value);
// Строка выше — имитация реального кода.
// То, что происходит в действительности, не выражается средствами C#.
}
}
Для удаления делегатов из цепочки применяется статический метод
Re move
объекта
Delegate
. Эта процедура демонстрируется в конце кода метода
Chain-
DelegateDemo1
:
fbChain = (Feedback) Delegate.Remove(
fbChain, new Feedback(FeedbackToMsgBox));
Метод
Remove
сканирует массив делегатов (с конца и до члена с нулевым индек-
сом), управляемый делегатом, на который ссылается первый параметр (в нашем
примере это
fbChain
). Он
ищет делегат, поля
_target
и
_methodPtr
которого совпа-
дают с соответствующими полями второго аргумента (в нашем примере это новый
делегат
Feedback
). При обнаружении совпадения, если в массиве осталось более
одного
элемента, создается новый делегат — создается массив
_invocationList
,
который инициализируется ссылкой на все элементы исходного массива за ис-
ключением удаляемого, — после чего возвращается ссылка на нового делегата. При
удалении последнего элемента цепочки метод
Remove
возвращает значение
null
.
Следует помнить,
что метод
Remove
за один раз удаляет лишь одного делегата, а не
все элементы с указанными значениями полей
_target
и
_methodPtr
.
Ранее мы также рассматривали делегат
Feedback
, возвращающий значение типа
void
. Однако этот делегат можно было определить и так:
public delegate Int32 Feedback(Int32 value);
В этом
случае псевдокод метода
Invoke
выглядел бы следующим образом:
public Int32 Invoke(Int32 value) {
Int32 result;
Delegate[] delegateSet = _invocationList as Delegate[];
if (delegateSet != null) {
// Массив указывает на делегаты, которые нужно вызвать
foreach (Feedback d in delegateSet)
result = d(value); // Вызов делегата
} else {
// Этот делегат определяет используемый метод обратного вызова.
// Этот метод вызывается для указанного объекта.
result = _methodPtr.Invoke(_target, value);
продолжение