Язык программирования C#9 и платформа .NET5
BinaryOp b = new BinaryOp(SimpleMath.Add);<b>// Вызвать метод Add() косвенно с использованием объекта делегата.</b>Console.WriteLine("10 + 10 is {0}", b(10, 10));Console.ReadLine();// Дополнительные определения типов должны находиться// в конце операторов верхнего уровня.// Этот делегат может указывать на любой метод,// принимающий два целых числа и возвращающий целое число.public delegate int BinaryOp(int x, int y);На заметку! Вспомните из главы 3, что дополнительные определения типов (делегат
в этом примере) должны располагаться после всех операторов верхнего уровня.BinaryOpИ снова обратите внимание на формат объявления типа делегата
; он определяет, что объекты делегатаBinaryOpмогут указывать на любой метод, принимающий два целочисленных значения и возвращающий целочисленное значение (действительное имя метода, на который он указывает, к делу не относится). Здесь мы создали класс по имениBinaryOp, определяющий два статических метода, которые соответствуют шаблону, определяемому делегатом BSimpleMath.inaryOpКогда вы хотите присвоить целевой метод заданному объекту делегата, просто передайте имя нужного метода конструктору делегата:
// Создать объект делегата BinaryOp, который// "указывает" на SimpleMath.Add().BinaryOp b = new BinaryOp(SimpleMath.Add);На данной стадии метод, на который указывает делегат, можно вызывать с использованием синтаксиса, выглядящего подобным прямому вызову функции:
// На самом деле здесь вызывается метод Invoke()!Console.WriteLine("10 + 10 is {0}", b(10, 10));"За кулисами" исполняющая среда вызывает сгенерированный компилятором метод
на вашем производном отInvoke()классе. В этом можно удостовериться, открыв сборку в утилитеMulticastDelegateи просмотрев код CIL внутри методаildasm.exe:Main().method private hidebysig static void Main(string[] args) cil managed{...callvirt instance int32 BinaryOp::Invoke(int32, int32)}Язык C# вовсе не требует явного вызова метода
внутри вашего кода. ПосколькуInvoke()может указывать на методы, которые принимают два аргумента, следующий оператор тоже допустим:BinaryOpConsole.WriteLine("10 + 10 is {0}", b.Invoke(10, 10));Вспомните, что делегаты .NET Core безопасны в отношении типов. Следовательно, если вы попытаетесь передать делегату метод, который не соответствует его шаблону, то получите ошибку на этапе компиляции. В целях иллюстрации предположим, что в классе
теперь определен дополнительный метод по имениSimpleMath, принимающий единственный целочисленный аргумент:SquareNumber()public class SimpleMath{public static int SquareNumber(int a) => a * a;}Учитывая, что делегат
может указывать только на методы, которые принимают два целочисленных значения и возвращают целочисленное значение, представленный ниже код некорректен и приведет к ошибке на этапе компиляции:BinaryOp// Ошибка на этапе компиляции! Метод не соответствует шаблону делегата!BinaryOp b2 = new BinaryOp(SimpleMath.SquareNumber);Исследование объекта делегата
Давайте усложним текущий пример, создав в классе
статический метод (по имениProgram). Он будет выводить на консоль имена методов, поддерживаемых объектом делегата, а также имя класса, определяющего метод. Для этого организуется итерация по массивуDisplayDelegatelnfо(), возвращенному методомSystem.Delegate, с обращением к свойствамGetlnvocationList()иTargetкаждого объекта:Methodstatic void DisplayDelegateInfo(Delegate delObj){// Вывести имена всех членов в списке вызовов делегата.foreach (Delegate d in delObj.GetInvocationList()){Console.WriteLine("Method Name: {0}", d.Method); // имя методаConsole.WriteLine("Type Name: {0}", d.Target); // имя типа}}Предполагая, что в метод
добавлен вызов нового вспомогательного метода:Main()BinaryOp b = new BinaryOp(SimpleMath.Add);DisplayDelegateInfo(b);вывод приложения будет таким:
***** Simple Delegate Example *****Method Name: Int32 Add(Int32, Int32)Type Name:10 + 10 is 20Обратите внимание, что при обращении к свойству
имя целевого класса (Target) в настоящий момент не отображается. Причина в том, что делегатSimpleMathуказывает на статический метод, и потому объект для ссылки попросту отсутствует! Однако если сделать методыBinaryOpиAdd()нестатическими (удалив ключевое словоSubstract()из их объявлений), тогда можно будет создавать экземпляр классаstatich и указывать методы для вызова с применением ссылки на объект:SimpleMat