Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

// Создать объект делегата BinaryOp, который

// "указывает" на SimpleMath.Add.

BinaryOp b = new BinaryOp(SimpleMath.Add);

// Вызвать метод 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
классе. В этом можно удостовериться, открыв сборку в утилите
ildasm.exe
и просмотрев код CIL внутри метода
Main
:

.method private hidebysig static void Main(string[] args) cil managed

{

...

callvirt instance int32 BinaryOp::Invoke(int32, int32)

}

Язык C# вовсе не требует явного вызова метода

Invoke
внутри вашего кода. Поскольку
BinaryOp
может указывать на методы, которые принимают два аргумента, следующий оператор тоже допустим:

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

Отправка уведомлений о состоянии объекта с использованием делегатов

Поделиться с друзьями: