В случае принятия такого подхода в итоге, как правило, получается несколько специальных делегатов, которые могут никогда не использоваться за рамками текущей задачи (например, MyGenericDelegate, CarEngineHandler и т.д.). Хотя вполне может быть и так, что для проекта требуется специальный уникально именованный делегат, в других ситуациях точное имя типа делегата роли не играет. Во многих случаях просто необходим "какой-нибудь делегат", который принимает набор аргументов и возможно возвращает значение, отличное от void. В таких ситуациях можно применять встроенные в инфраструктуру делегаты Action<> и Func<>. Чтобы удостовериться в их полезности, создайте новый проект консольного приложения по имени ActionAndFuncDelegates.

Обобщенный делегат Action<> определен в пространствах имен System. Его можно использовать для "указания" на метод, который принимает вплоть до 16 аргументов (чего должно быть вполне достаточно!) и возвращает void. Вспомните, что поскольку Action<> является обобщенным делегатом, понадобится также указывать типы всех параметров.

Модифицируйте класс Program, определив в нем новый статический метод, который принимает (скажем) три уникальных параметра:

// Это цель для делегата Action<>.

static void DisplayMessage(string msg, ConsoleColor txtColor,

                           int printCount)

{

  // Установить цвет текста консоли.

  ConsoleColor previous = Console.ForegroundColor;

  Console.ForegroundColor = txtColor;

  for (int i = 0; i < printCount; i++)

  {

    Console.WriteLine(msg);

  }

  // Восстановить цвет.

  Console.ForegroundColor = previous;

}

Теперь вместо построения специального делегата вручную для передачи потока программы методу DisplayMessage() вы можете применять готовый делегат Action<>:

Console.WriteLine("***** Fun with Action and Func *****");

// Использовать делегат Action<> для указания на метод DisplayMessage().

Action actionTarget =

  DisplayMessage;

actionTarget("Action Message!", ConsoleColor.Yellow, 5);

Console.ReadLine();

Как видите, при использовании делегата Action<> не нужно беспокоиться об определении специального типа делегата. Тем не менее, как уже упоминалось, тип делегата Action<> позволяет указывать только на методы, возвращающие void. Если необходимо указывать на метод, имеющий возвращаемое значение (и нет желания заниматься написанием собственного типа делегата), тогда можно применять тип делегата Func<>.

Обобщенный делегат Funс<> способен указывать на методы, которые (подобно Action<>) принимают вплоть до 16 параметров и имеют специальное возвращаемое значение. В целях иллюстрации добавьте в класс Program новый метод:

// Цель для делегата Func<>.

static int Add(int x, int y)

{

  return x + y;

}

Ранее в главе был построен специальный делегат BinaryOp для "указания" на методы сложения и вычитания. Теперь задачу можно упростить за счет использования версии Func<>, которая принимает всего три параметра типа. Учтите, что последний параметр в Func<> всегда представляет возвращаемое значение метода. Чтобы закрепить данный момент, предположим, что в классе Program также определен следующий метод:

static string SumToString(int x, int y)

{

  return (x + y).ToString();

}

Вызовите эти методы:

Func funcTarget = Add;

int result = funcTarget.Invoke(40, 40);

Console.WriteLine("40 + 40 = {0}", result);

Func funcTarget2 = SumToString;

string sum = funcTarget2(90, 300);

Console.WriteLine(sum);

С учетом того, что делегаты Action<> и Func<> могут устранить шаг по ручному определению специального делегата, вас может интересовать, должны ли они применяться всегда. Подобно большинству аспектов программирования ответ таков: в зависимости от ситуации. Во многих случаях Action<> и Func<> будут предпочтительным вариантом. Однако если необходим делегат со специальным именем, которое, как вам кажется, помогает лучше отразить предметную область, то построение специального делегата сводится к единственному оператору кода. В оставшихся материалах книги вы увидите оба подхода.

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

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