// группового преобразования метода.

   MyGenericDelegate‹int› intTarget = IntTarget;

   intTarget(9);

   Console.ReadLine();

  }

  static void StringTarget(string arg) {

   Console.WriteLine("arg в верхнем регистре: {0}", arg.ToUpper());

  }

  static void IntTarget(int arg) {

   Console.WriteLine("++arg: {0}", ++arg);

  }

 }

}

Обратите внимание на то. что MyGenericDelegate‹T› определяет один пара-метр типа, представляющий аргумент, отправляемый целевому объекту делегата. При создании экземпляра этого типа требуется конкретизировать значение параметра типа, а также имя метода, вызываемого делегатом. Так, если вы укажете строковый тип, то отправите целевому методу строковое значение.

// Создание экземпляра MyGenericDelegate‹T›

// со значением string для параметра типа.

MyGenericDelegate‹string› strTarget = new MyGenericDelegate‹string›(StringTarget);

strTarget("Некоторые строковые данные");

С учетом формата объекта strTarget метод StringTarget() должен теперь получить в качестве параметра одну строку.

static void StringTarget(string arg) {

 Console.WriteLine("arg в верхнем регистре: {0}", arg.ToUpper());

}

<p>Имитация обобщенных делегатов в .NET 1.1</p>

Как видите, обобщенные делегаты предлагают более гибкий подход для указания вызываемых методов. В рамках .NET 1.1 аналогичного результата можно достичь с помощью базового System.Object.

public delegate void MyDelegate(object arg);

Это позволяет посылать любой тип данных целевому объекту делегата, но при этом не гарантируется типовая безопасность, а кроме того, остаются нерешенными проблемы создания объектных образов. Предположим, например, что мы создали два экземпляра MyDelegate, которые указывают на один и тот же метод MyTarget. Обратите внимание на проблемы создания объектных образов и восстановления значений, а также на отсутствие гарантий типовой безопасности.

class Program {

 static void Main(string[] args) {

  …

  // Регистрация цели с помощью

  // 'традиционного' синтаксиса делегата.

  MyDelegate d = new MyDelegate(MyTarget) d("Дополнительные строковые данные");

  // Регистрация цели с помощью

  // группового преобразования метода.

  MyDelegate d2 = MyTarget;

  d2(9); // Проблема объектного образа.

  …

 }

 // Ввиду отсутствия типовой безопасности мы должны

 // определить соответствующий тип до преобразования.

 static void MyTarget(object arg) {

  if (arg is int) {

   int i = (int)arg; // Проблема восстановления значения.

   Console.WriteLine("++arg: {0}", ++i);

  }

  if (arg is string) {

   string s = (string) arg;

   Console.WriteLine("arg в верхнем регистре: {0}", s.ToUpper());

  }

 }

}

Когда вы посылаете целевому объекту тип, характеризуемый значением, это значение (конечно же) "упаковывается" в объект и "распаковывается" после его получения целевым методом. Точно так же, поскольку поступающий параметр может быть всем чем угодно, вы должны динамически проверить соответствующий тип перед тем, как выполнить преобразование. С помощью обобщенных делегатов вы можете получить всю необходимую гибкость без "проблем".

<p>Несколько слов о вложенных делегатах</p>

Завершим эту главу рассмотрением еще одного аспекта обобщенных делегатов. Вы знаете, что делегаты могут быть вложены в тип класса, что должно означать тесную ассоциацию между этими двумя ссылочными типами. Если тип-контейнер при этом оказывается обобщенным, вложенный делегат может использовать в своем определении параметры типа.

// Вложенные обобщающе делегаты могут иметь доступ к параметрам

// обобщенного типа-контейнера.

public class MyList‹T› {

 private List‹T› listOfData = new List‹T›();

Перейти на страницу:

Похожие книги