Глава.17 .Делегаты
// Строка выше — имитация реального кода.
// То, что происходит в действительности, не выражается средствами C#.
}
return result;
}
По мере вызова отдельных делегатов возвращаемое значение сохраняется в пере-
менной
result
. После завершения цикла в этой переменной оказывается только
результат вызова последнего делегата (предыдущие возвращаемые значения от-
брасываются); именно это значение возвращается коду, вызвавшему метод
Invoke
.
Поддержка цепочек делегатов в C#
Чтобы упростить задачу разработчиков, компилятор C# автоматически предостав-
ляет перегруженные версии операторов
+=
и
-=
для экземпляров делегатов. Эти
операторы вызывают методы
Delegate.Combine
и
Delegate.Remove
соответственно.
Они упрощают построение цепочек делегатов. В результате компиляции методов
ChainDelegateDemo1
и
ChainDelegateDemo2
(см. пример в начале главы) получается
идентичный IL-code. Единственная разница в том, что благодаря операторам
+=
и
-=
исходный код метода
ChainDelegateDemo2
получается проще.
Для доказательства идентичности сгенерируйте IL-код обоих методов и изучите
его при помощи утилиты
ILDasm exe
. Вы убедитесь, что компилятор C# действи-
тельно заменяет все операторы
+=
и
-=
вызовами статических методов
Combine
и
Remove
типа
Delegate
соответственно.
дополнительные средства управления
цепочками делегатов
Итак, вы научились создавать цепочки делегатов и вызывать все их компоненты.
Последняя возможность реализуется благодаря наличию в методе
Invoke
кода,
просматривающего все элементы массива делегатов. И хотя этого простого алго-
ритма хватает для большинства сценариев, у него есть ряд ограничений. К примеру,
сохраняется только последнее из значений, возвращаемых методами обратного
вызова. Получить все остальные значения нельзя. И это не единственное ограниче-
ние. Скажем, в ситуации, когда один из делегатов в цепочке становится причиной
исключения или блокируется на очень долгое время, выполнение цепочки останав-
ливается. Понятно, что данный алгоритм не отличается надежностью.
В качестве альтернативы можно воспользоваться экземплярным методом
GetInvocationList
класса
MulticastDelegate
. Этот метод позволяет в явном виде
вызвать любой из делегатов в цепочке:
public abstract class MulticastDelegate : Delegate {
// Создает массив, каждый элемент которого ссылается
// на делегата в цепочке
public sealed override Delegate[] GetInvocationList();
}
449
Обратный.вызов.нескольких.методов.(цепочки.делегатов)
Метод
GetInvocationList
работает с объектами классов, производных от
MulticastDelegate
. Он возвращает массив ссылок, каждая из которых указывает
на какой-то делегат в цепочке. По сути, этот метод создает массив и инициализи-
рует его элементы ссылками на соответствующие делегаты; в конце возвращается
ссылка на этот массив. Если поле
_invocationList
содержит
null
, возвращаемый
массив будет содержать всего один элемент, ссылающийся на единственного деле-
гата в цепочке — экземпляр самого делегата.
Написать алгоритм, в явном виде вызывающий каждый элемент массива, не-
сложно:
using System;
using System.Reflection;
using System.Text;
// Определение компонента Light
internal sealed class Light {
// Метод возвращает состояние объекта Light
public String SwitchPosition() {
return "The light is off";
}
}
// Определение компонента Fan
internal sealed class Fan {
// Метод возвращает состояние объекта Fan
public String Speed() {
throw new InvalidOperationException("The fan broke due to overheating");
}
}
// Определение компонента Speaker
internal sealed class Speaker {
// Метод возвращает состояние объекта Speaker
public String Volume() {
return "The volume is loud";
}
}
public sealed class Program {
// Определение делегатов, позволяющих запрашивать состояние компонентов
private delegate String GetStatus();
public static void Main() {
// Объявление пустой цепочки делегатов
GetStatus getStatus = null;
// Создание трех компонентов и добавление в цепочку
// методов проверки их состояния
getStatus += new GetStatus(new Light().SwitchPosition);
getStatus += new GetStatus(new Fan().Speed);
продолжение
450
Do'stlaringiz bilan baham: |