Студопедия

КАТЕГОРИИ:

АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника


Делегаты и отражение




Все примеры использования делегатов, показанные до сих пор, требовали, чтобы разработчик заранее знал прототип метода обратного вызова. Так, если fb — переменная, ссылающаяся на делегат Feedback, код для вызова делегата будет примерно такой:

fb(item); // Параметр item определен как Int32.

Как видите, во время кодирования разработчик должен знать, сколько параметров требует метод обратного вызова и тип каждого из них. К счастью, у вас почти всегда есть эта информация, поэтому написать код вроде предыдущего — не проблема.

Но порой у разработчика нет этих сведений на момент компиляции. Достаточно вспомнить пример из главы 10, в котором обсуждался тип EventSet. В этом примере словарь поддерживается набором разных типов делегатов. Чтобы событие сработало во время выполнения, производится поиск и вызов делегата из словаря. При компиляции нельзя точно узнать, какой делегат будет вызван и какие параметры передать его методу обратного вызова.

К счастью, тип System.Delegate предлагает методы, позволяющие создавать и вызывать делегаты, даже если на момент компиляции нет всех сведений о делегате. Вот эти методы:

public abstract class Delegate

{

// Создать делегат «тип», служащий оболочкой заданного статического метода.

public static Delegate CreateDelegate(Type type, Methodlnfo method);

public static Delegate CreateDelegate(

Type type,

Methodlnfo method,

Boolean throwOnBindFailure

);

 

// Создать делегат «тип», служащий оболочкой заданного экземплярного метода.

public static Delegate CreateDelegate(

Type type, // Эквивалентно this

Object firstArgument,

Methodlnfo method

);

public static Delegate CreateDelegate(

Type type,

Object firstArgument,

Methodlnfo method,

Boolean throwOnBindFailure

);

 

// Вызываем делегат, передав ему параметры.

public Object DynamicInvoke(params Object[] args);

}

Все версии метода CreateDelegate создают новый объект типа — потомок Delegate, заданного первым параметром type.

Параметр Methodlnfo указывает, что метод должен вызываться по методу обратного вызова; для получения этого значения нужно использовать API отражения (см. главу 22). Если делегат должен быть оболочкой для экземплярного метода, надо передать в CreateDelegate параметр firstArgument, указывающий, что объект нужно передать в экземплярный метод в параметре this (первый аргумент).

Наконец, CreateDelegate генерирует исключение ArgumentException, если делегат не может связаться с методом, указанным в параметре method. Это может произойти, если сигнатура метода method, заданная переменной, не соответствует сигнатуре, требуемой делегатом и заданной в параметре type. Однако если передать в параметре throwOnBindPailure значение false, вместо этого будет возвращено значение null.

Внимание! У класса System.Delegate намного больше перегруженных версий метода CreateDelegate, чем показано здесь. Никогда не следует вызывать ни один из этих других методов. В Microsoft даже жалеют, что вообще определили их. Причина в том, что в остальных методах в процессе привязки вызываемый метод идентифицируется с использованием типа String, а не Metbodlnfo. Это означает, что возможна неоднозначная привязка, приводящая к непрогнозируемому поведению приложения.

Метод Dynamiclnvoke типа SystemDelegate позволяет вызвать метод обратного вызова объекта-делегата, передав набор параметров, определяемых во время выполнения. При вызове Dynamiclnvoke его код проверяет совместимость переданных параметров с параметрами, ожидаемыми методом обратного вызова. Если параметры совместимы, выполняется обратный вызов, в противном случае генерируется исключение ArgumentException.

Dynamiclnvoke возвращает объект, который вернул метод обратного вызова. Следующий код демонстрирует использование методов CreateDelegate и Dynamiclnvoke:

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 Program

{

public static void Main(String[] args)

{

if (args.Length < 2)

{

 

String fileName =

Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);

String usage = @"Usage:" +

"{0} {1} 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} {1} TwoInt32s Add 123 321" +

"{0} {1} TwoInt32s Subtract 123 321" +

"{0} {1} OneString NumChars Y'Hello there\"" +

"{0} {1} OneString Reverse Y'Hello there\"";

Console.WriteLine(usage, Environment.NewLine, fileName);

return;

 

// Преобразовываем параметр delType в тип делегата.

Type delType = Type.GetType(args[0]);

 

if (delType == null)

{

Console.WriteLine("Invalid delType argument: " + args[0]);

return;

}

Delegate d;

 

try

{

// Преобразовываем параметр Arg1 в метод.

MethodInfo mi = typeof(Program).GetMethod(args[1], BindingFlags.NonPublic | BindingFlags.Static);

// Создаем объект-делегат, служащий оболочкой статического метода.

d = Delegate.CreateDelegate(delType, mi);

}

 

catch (ArgumentException)

{

Console.WriteLine("Invalid methodName argument: " + args[1]);

return;

}

// Создаем массив, который содержит только параметры,

// передаваемые методу через объект-делегат.

Object[] callbackArgs = new Object[args.Length - 2];

 

if (d.GetType() == typeof(TwoInt32s))

{

try

{

// Преобразуем параметры типа String в параметры типа Int32.

for (Int32 a = 2; a < args.Length; a++)

callbackArgs[a - 2] = Int32.Parse(args[a]);

}

catch (FormatException)

{

Console.WriteLine("Parameters must be integers."); return;

}

}

 

if (d.GetType() == typeof(OneString))

{

// Просто копируем параметр типа String.

Array.Copy(args, 2, callbackArgs, 0, callbackArgs.Length);

}

 

try

{

// Вызываем делегат и показываем результат.

Object result = d.DynamicInvoke(callbackArgs);

Console.WriteLine("Result = " + result);

}

 

catch (TargetParameterCountException)

{

Console.WriteLine("Incorrect number of parameters specified.");

}

}

}

 

// Это метод обратного вызова, принимающий два параметра Int32.

private static Object Add(Int32 n1, Int32 n2)

{

return n1 + n2;

}

 

// Это метод обратного вызова, принимающий два параметра Int32.

private static Object Subtract(Int32 n1, Int32 n2)

{

return n1 - n2;

}

 

// Это метод обратного вызова, принимающий один параметр String.

private static Object NumChars(String s1)

{

return s1.Length;

}

 

// Это метод обратного вызова, принимающий один параметр String.

private static Object Reverse(String s1)

{

Char[] chars = s1.ToCharArray();

Array.Reverse(chars);

return new String(chars);

}

}



Поделиться:

Дата добавления: 2015-04-21; просмотров: 115; Мы поможем в написании вашей работы!; Нарушение авторских прав





lektsii.com - Лекции.Ком - 2014-2024 год. (0.006 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты