// Этот делегат может указывать на любой метод, который принимает
// два целочисленных значения и возвращает целочисленное значение.
public delegate int BinaryOp(int x, int y);
Когда компилятор C# обрабатывает тип делегата, он автоматически генерирует запечатанный (
sealed
) класс, производный от
System.MulticastDelegate
. Этот класс (в сочетании со своим базовым классом
System.Delegate
) предоставляет необходимую инфраструктуру для делегата, которая позволяет хранить список методов, подлежащих вызову в будущем. Например, если вы изучите делегат
BinaryOp
с помощью утилиты
ildasm.exe
, то обнаружите показанные ниже детали (вскоре вы построите полный пример):
// -------------------------------------------------------
// TypDefName: SimpleDelegate.BinaryOp
// Extends : System.MulticastDelegate
// Method #1
// -------------------------------------------------------
// MethodName: .ctor
// ReturnType: Void
// 2 Arguments
// Argument #1: Object
// Argument #2: I
// Method #2
// -------------------------------------------------------
// MethodName: Invoke
// ReturnType: I4
// 2 Arguments
// Argument #1: I4
// Argument #2: I4
// 2 Parameters
// (1) ParamToken : Name : x flags: [none]
// (2) ParamToken : Name : y flags: [none] //
// Method #3
// -------------------------------------------------------
// MethodName: BeginInvoke
// ReturnType: Class System.IAsyncResult
// 4 Arguments
// Argument #1: I4
// Argument #2: I4
// Argument #3: Class System.AsyncCallback
// Argument #4: Object
// 4 Parameters
// (1) ParamToken : Name : x flags: [none]
// (2) ParamToken : Name : y flags: [none]
// (3) ParamToken : Name : callback flags: [none]
// (4) ParamToken : Name : object flags: [none]
//
// Method #4
// -------------------------------------------------------
// MethodName: EndInvoke
// ReturnType: I4 (int32)
// 1 Arguments
// Argument #1: Class System.IAsyncResult
// 1 Parameters
// (1) ParamToken : Name : result flags: [none]
Как видите, в сгенерированном компилятором классе
BinaryOp
определены три открытых метода. Главным методом в .NET Core является
Invoke()
, т.к. он используется для вызова каждого метода, поддерживаемого объектом делегата, в синхронной манере; это означает, что вызывающий код должен ожидать завершения вызова, прежде чем продолжить свою работу. Довольно странно, но синхронный метод
Invoke()
может не нуждаться в явном вызове внутри вашего кода С#. Вскоре будет показано, что
Invoke()
вызывается "за кулисами", когда вы применяете соответствующий синтаксис С#.
На заметку! Несмотря на то что методы
BeginInvoke()
и
EndInvoke()
генерируются, они не поддерживаются при запуске вашего кода под управлением .NET Core. Это может разочаровывать, поскольку в случае их использования вы получите ошибку не на этапе компиляции, а во время выполнения.
Так благодаря чему же компилятор знает, как определять метод
Invoke()
? Для понимания процесса ниже приведен код сгенерированного компилятором класса
BinaryOp
(полужирным курсивом выделены элементы, указанные в определении типа делегата):
sealed class <b>BinaryOp</b> : System.MulticastDelegate
{
public <b>int</b> Invoke(<b>int x, int y</b>);
...
}
Первым делом обратите внимание, что параметры и возвращаемый тип для метода
Invoke()
в точности соответствуют определению делегата
BinaryOp
.
Давайте рассмотрим еще один пример. Предположим, что определен тип делегата, который может указывать на любой метод, возвращающий значение
string
и принимающий три входных параметра типа
System.Boolean
:
public delegate string MyDelegate (bool a, bool b, bool c);
На этот раз сгенерированный компилятором класс можно представить так:
sealed class <b>MyDelegate</b> : System.MulticastDelegate
{
public <b>string</b> Invoke(<b>bool a, bool b, bool c</b>);