ВниМание
Без.сомнения,.у.любого.программиста.возникает.соблазн.использовать.лямбда-
выражения.там,.где.это.уместно.и.не.уместно .Лично.я.привык..к.ним.не.сразу .Ведь.
код,.который.вы.пишете.внутри.метода,.на.самом.деле.этому.методу.не.принадлежит,.
что.затрудняет.отладку.и.пошаговое.выполнение .Хотя.я.был.откровенно.поражен.
тем,.что.отладчик.Visual.Studio.позволял.выполнять.лямбда-выражения.в.моем.ис-
ходном.коде.в.пошаговом.режиме
Я.установил.для.себя.правило:.если.в.методе.обратного.вызова.предполагается.более.
трех.строк.кода,.не.использовать.лямбда-выражения .Вместо.этого.я.пишу.метод.
вручную.и.присваиваю.ему.имя .Впрочем,.при.разумном.подходе.лямбда-выражения.
способны.серьезно.повысить.продуктивность.работы.программиста.и.упростить.под-
держку.кода .В.следующем.примере.кода.лямбда-выражения.смотрятся.естественно,.
и.без.них.написание,.чтение.и.редактирование.кода.было.бы.намного.сложнее:
// Создание и инициализация массива String
String[] names = { "Jeff", "Kristin", "Aidan", "Grant" };
// Извлечение имен со строчной буквой 'a'
Char charToFind = 'a';
продолжение
460
Глава.17 .Делегаты
names = Array.FindAll(names, name => name.IndexOf(charToFind) >= 0);
// Преобразование всех символов строки в верхний регистр
names = Array.ConvertAll(names, name => name.ToUpper());
// Вывод результатов
Array.ForEach(names, Console.WriteLine);
делегаты и отражение
Все показанные в этой главе примеры использования делегатов требовали, чтобы
разработчик заранее знал прототип метода обратного вызова. Скажем, если пере-
менная
fb
ссылается на делегата
Feedback
(см. листинг первого примера в этой
главе), код обращения к делегату мог бы выглядеть примерно так:
fb(item); // параметр item определен как Int32
Как видите, разработчик должен знать количество и тип параметров метода об-
ратного вызова. К счастью, эта информация почти всегда доступна разработчику,
так что написать подобный этому код — не проблема.
Впрочем, в отдельных редких ситуациях на момент компиляции эти сведения от-
сутствуют. В главе 11 при обсуждении типа
EventSet
приводился соответствующий
пример со словарем, хранящим набор разных типов делегатов. Для вызова события
во время выполнения производился поиск и вызов делегата из словаря. Однако при
этом было невозможно узнать во время компиляции, какой делегат будет вызван
и какие параметры следует передать его методу обратного вызова.
К счастью, в классе
System.Reflection.MethodInfo
имеется метод
Create-
Delegate
, позволяющий создавать и вызывать делегаты даже при отсутствии
сведений о них на момент компиляции. Вот как выглядят перегруженные версии
этого метода:
public abstract class MethodInfo : MethodBase {
// Создание делегата, служащего оберткой статического метода.
public virtual Delegate CreateDelegate(Type delegateType);
// Создание делегата, служащего оберткой экземплярного метода;
// target ссылается на аргумент 'this'.
public virtual Delegate CreateDelegate(Type delegateType, Object target);
}
После того как делегат будет создан, его можно вызвать методом
DynamicInvoke
класса
Delegate
, который выглядит примерно так:
public abstract class Delegate {
// Вызов делегата с передачей параметров
public Object DynamicInvoke(params Object[] args);
}
461
Делегаты.и.отражение
При использовании API отражения (см. главу 23) необходимо сначала получить
объект
MethodInfo
для метода, для которого требуется создать делегата. Затем вы-
зов метода
CreateDelegate
создает новый объект типа, производного от
Delegate
и определяемого первым параметром
delegateType
. Если делегат представляет
экземплярный метод, также следует передать
CreateDelegate
параметр
target
,
обозначающий объект, который должен передаваться экземплярному методу как
параметр
this
.
Метод
DynamicInvoke
класса
System.Delegate
позволяет задействовать метод
обратного вызова делегата, передавая набор параметров, определяемых во вре-
мя выполнения. При вызове метода
DynamicInvoke
проверяется совместимость
переданных параметров с параметрами, ожидаемыми методом обратного вызова.
Если параметры совместимы, выполняется обратный вызов; в противном случае
генерируется исключение
ArgumentException
. Данный метод возвращает объект,
который был возвращен методом обратного вызова.
Рассмотрим пример применения методов
CreateDelegate
и
DynamicInvoke
:
using System;
using System.Reflection;
using System.IO;
// Несколько разных определений делегатов
internal delegate Object TwoInt32s(Int32 n1, Int32 n2);
internal delegate Object OneString(String s1);
public static class DelegateReflection {
public static void Main(String[] args) {
if (args.Length < 2) {
String usage =
@"Usage:" +
"{0} delType methodName [Arg1] [Arg2]" +
"{0} where delType must be TwoInt32s or OneString" +
"{0} if delType is TwoInt32s, methodName must be Add or Subtract" +
"{0} if delType is OneString, methodName must be NumChars or Reverse"
+
"{0}" +
"{0}Examples:" +
"{0} TwoInt32s Add 123 321" +
"{0} TwoInt32s Subtract 123 321" +
"{0} OneString NumChars \"Hello there\"" +
"{0} OneString Reverse \"Hello there\"";
Console.WriteLine(usage, Environment.NewLine);
return;
}
// Преобразование аргумента delType в тип делегата
Type delType = Type.GetType(args[0]);
if (delType == null) {
Console.WriteLine("Invalid delType argument: " + args[0]);
продолжение
462
Do'stlaringiz bilan baham: |