рис. 17.5.
.Делегаты.после.вставки.в.цепочку.второго.члена
Для добавления в цепочку третьего делегата снова вызывается метод
Combine
:
fbChain = (Feedback) Delegate.Combine(fbChain, fb3);
И снова, видя, что переменная
fbChain
уже ссылается на делегата, метод соз-
дает очередного делегата, как показано на рис. 17.6. Как и в предыдущих случаях,
новый делегат присваивает начальные значения своим закрытым полям
_target
и
_methodPtr
, в то время как поле
_invocationList
инициализируется ссылкой на
массив делегатов. Первому и второму элементам массива (с индексами 0 и 1) при-
сваиваются ссылки на те же делегаты, на которые ссылался предыдущий делегат.
Третий элемент массива (с индексом 2) становится ссылкой на делегата, служащего
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);
продолжение
448
Do'stlaringiz bilan baham: |