Язык программирования C#9 и платформа .NET5
// возвращает void и принимает единственный параметр типа Т.public delegate void MyGenericDelegate<T>(T arg);Как видите, в типе делегата
определен единственный параметр, представляющий аргумент для передачи цели делегата. При создании экземпляра этого типа должно быть указано значение параметра типа наряду с именем метода, который делегат может вызывать. Таким образом, если указать типMyGenericDelegate<T>, тогда целевому методу будет отправляться строковое значение:string// Создать экземпляр MyGenericDelegate<T>// с указанием string в качестве параметра типа.
MyGenericDelegate<string> strTarget = StringTarget;strTarget("Some string data");С учетом формата объекта
методstrTargetтеперь должен принимать в качестве параметра единственную строку:StringTargetstatic void StringTarget(string arg){Console.WriteLine("arg in uppercase is: {0}", arg.ToUpper());}Обобщенные делегаты Action<> и Func<>
В настоящей главе вы уже видели, что когда нужно применять делегаты для обратных вызовов в приложениях, обычно должны быть выполнены описанные далее шаги.
1. Определить специальный делегат, соответствующий формату метода, на который он указывает.
2. Создать экземпляр специального делегата, передав имя метода в качестве аргумента конструктора.
3. Косвенно обратиться к методу через вызов
на объекте делегата.Invoke()В случае принятия такого подхода в итоге, как правило, получается несколько специальных делегатов, которые могут никогда не использоваться за рамками текущей задачи (например,
,MyGenericDelegate<T>и т.д.). Хотя вполне может быть и так, что для проекта требуется специальный уникально именованный делегат, в других ситуациях точное имя типа делегата роли не играет. Во многих случаях просто необходим "какой-нибудь делегат", который принимает набор аргументов и возможно возвращает значение, отличное отCarEngineHandler. В таких ситуациях можно применять встроенные в инфраструктуру делегатыvoidиAction<>. Чтобы удостовериться в их полезности, создайте новый проект консольного приложения по имениFunc<>.ActionAndFuncDelegatesОбобщенный делегат
определен в пространствах именAction<>. Его можно использовать для "указания" на метод, который принимает вплоть до 16 аргументов (чего должно быть вполне достаточно!) и возвращаетSystem. Вспомните, что поскольку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 *****");<b>// Использовать делегат Action<> для указания на метод DisplayMessage().</b>Action<string, ConsoleColor, int> actionTarget =DisplayMessage;actionTarget("Action Message!", ConsoleColor.Yellow, 5);Console.ReadLine();Как видите, при использовании делегата
не нужно беспокоиться об определении специального типа делегата. Тем не менее, как уже упоминалось, тип делегатаAction<>позволяет указывать только на методы, возвращающиеAction<>. Если необходимо указывать на метод, имеющий возвращаемое значение (и нет желания заниматься написанием собственного типа делегата), тогда можно применять тип делегатаvoid.Func<>Обобщенный делегат
способен указывать на методы, которые (подобноFunс<>) принимают вплоть до 16 параметров и имеют специальное возвращаемое значение. В целях иллюстрации добавьте в классAction<>новый метод:Program// Цель для делегата Func<>.static int Add(int x, int y){return x + y;}Ранее в главе был построен специальный делегат
для "указания" на методы сложения и вычитания. Теперь задачу можно упростить за счет использования версииBinaryOp, которая принимает всего три параметра типа. Учтите, что последний параметр вFunc<>всегда представляет возвращаемое значение метода. Чтобы закрепить данный момент, предположим, что в классеFunc<>также определен следующий метод:Program