// Вызвать метод Add косвенно с использованием объекта делегата.
Console.WriteLine("10 + 10 is {0}", b(10, 10));
Console.ReadLine;
// Дополнительные определения типов должны находиться
// в конце операторов верхнего уровня.
// Этот делегат
может указывать на любой метод,
// принимающий два целых числа и возвращающий целое число.
public delegate int BinaryOp(int x, int y);
На заметку! Вспомните из главы 3, что дополнительные определения типов (делегат
BinaryOp
в этом примере) должны располагаться после всех операторов верхнего уровня.
И снова обратите внимание на формат объявления типа делегата
BinaryOp
; он определяет, что объекты делегата
BinaryOp
могут указывать на любой метод, принимающий два целочисленных значения и возвращающий целочисленное значение (действительное имя метода, на который он указывает, к делу не относится). Здесь мы создали класс по имени
SimpleMath
, определяющий два статических метода, которые соответствуют шаблону, определяемому делегатом B
inaryOp
.
Когда вы хотите присвоить целевой метод заданному объекту делегата, просто передайте имя нужного метода конструктору делегата:
// Создать объект делегата BinaryOp, который
// "указывает" на SimpleMath.Add.
BinaryOp b = new BinaryOp(SimpleMath.Add);
На данной стадии метод, на который указывает делегат, можно вызывать с использованием синтаксиса, выглядящего подобным прямому вызову функции:
// На самом деле здесь вызывается метод Invoke!
Console.WriteLine("10 + 10 is {0}", b(10, 10));
"За кулисами" исполняющая среда вызывает сгенерированный компилятором метод
Invoke
на вашем производном от
MulticastDelegate
классе. В этом можно удостовериться, открыв сборку в утилите
может указывать на методы, которые принимают два аргумента, следующий оператор тоже допустим:
Console.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
и
Method
каждого объекта:
static 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
нестатическими (удалив ключевое слово
static
из их объявлений), тогда можно будет создавать экземпляр класса
SimpleMat
h и указывать методы для вызова с применением ссылки на объект:
using System;
using SimpleDelegate;
Console.WriteLine("***** Simple Delegate Example *****\n");
// Делегаты могут также указывать на методы экземпляра.
SimpleMath m = new SimpleMath;
BinaryOp b = new BinaryOp(m.Add);
// Вывести сведения об объекте.
DisplayDelegateInfo(b);
Console.WriteLine("10 + 10 is {0}", b(10, 10));
Console.ReadLine;
В данном случае вывод будет выглядеть следующим образом:
***** Simple Delegate Example *****
Method Name: Int32 Add(Int32, Int32)
Type Name: SimpleDelegate.SimpleMath
10 + 10 is 20
Отправка уведомлений о состоянии объекта с использованием делегатов