...
}
Делегаты могут также "указывать" на методы, которые содержат любое количество параметров
out
и
ref
(а также параметры типа массивов, помеченные с помощью ключевого слова
params
). Пусть имеется следующий тип делегата:
public delegate string MyOtherDelegate(out bool a, ref bool b, int c);
Сигнатура метода
Invoke()
выглядит вполне ожидаемо.
Подводя итоги, отметим, что определение типа делегата C# дает в результате запечатанный класс со сгенерированным компилятором методом, в котором типы параметров и возвращаемые типы основаны на объявлении делегата. Базовый шаблон может быть приближенно описан с помощью следующего псевдокода:
// Это лишь псевдокод!
public sealed class <i>ИмяДелегата</i> : System.MulticastDelegate
{
public <i>возвращаемоеЗначениеДелегата</i>
Invoke(<i>всеВходныеRefиOutПараметрыДелегата</i>);
}
Базовые классы System.MulticastDelegate и System.Delegate
Итак, когда вы строите тип с применением ключевого слова
delegate
, то неявно объявляете тип класса, производного от
System.MulticastDelegate
. Данный класс предоставляет своим наследникам доступ к списку, который содержит адреса методов, поддерживаемых типом делегата, а также несколько дополнительных методов (и перегруженных операций) для взаимодействия со списком вызовов. Ниже приведены избранные методы класса
System.MulticastDelegate
:
public abstract class MulticastDelegate : Delegate
{
<b> // Возвращает список методов, на которые "указывает" делегат.</b>
public sealed override Delegate[] GetInvocationList();
<b> // Перегруженные операции.</b>
public static bool operator ==
(MulticastDelegate d1, MulticastDelegate d2);
public static bool operator !=
(MulticastDelegate d1, MulticastDelegate d2);
<b> // Используются внутренне для управления списком методов,</b>
<b> // поддерживаемых делегатом.</b>
private IntPtr _invocationCount;
private object _invocationList;
}
Класс
System.MulticastDelegate
получает дополнительную функциональность от своего родительского класса
System.Delegate
. Вот фрагмент его определения:
public abstract class Delegate : ICloneable, ISerializable
{
<b> // Методы для взаимодействия со списком функций.</b>
public static Delegate Combine(params Delegate[] delegates);
public static Delegate Combine(Delegate a, Delegate b);
public static Delegate Remove(
Delegate source, Delegate value);
public static Delegate RemoveAll(
Delegate source, Delegate value);
<b> // Перегруженные операции.</b>
public static bool operator ==(Delegate d1, Delegate d2);
public static bool operator !=(Delegate d1, Delegate d2);
<b> // Свойства, открывающие доступ к цели делегата.</b>
public MethodInfo Method { get; }
public object Target { get; }
}
Имейте в виду, что вы никогда не сможете напрямую наследовать от таких базовых классов в своем коде (попытка наследования приводит к ошибке на этапе компиляции). Тем не менее, когда вы используете ключевое слово
delegate
, то тем самым неявно создаете класс, который "является"
MulticastDelegate
. В табл. 12.1 описаны основные члены, общие для всех типов делегатов.
Пример простейшего делегата
На первый взгляд делегаты могут показаться несколько запутанными. Рассмотрим для начала простой проект консольного приложения (по имени
SimpleDelegate
), в котором применяется определенный ранее тип делегата
BinaryOp
. Ниже показан полный код с последующим анализом:
// SimpleMath.cs
namespace SimpleDelegate
{
// Этот класс содержит методы, на которые
// будет указывать BinaryOp.
public class SimpleMath
{
public static int Add(int x, int y) => x + y;
public static int Subtract(int x, int y) => x - y;
}
}
// Program.cs
using System;
using SimpleDelegate;
Console.WriteLine("***** Simple Delegate Example *****\n");
<b>// Создать объект делегата BinaryOp, который</b>
<b>// "указывает" на SimpleMath.Add().</b>