Использование делегатов (Руководство по программированию на C#)Using Delegates (C# Programming Guide)

Делегат — это тип, который безопасно инкапсулирует метод, схожий с указателем функции в C и C++.A delegate is a type that safely encapsulates a method, similar to a function pointer in C and C++. В отличие от указателей функций в C делегаты объектно-ориентированы, типобезопасны и безопасны.Unlike C function pointers, delegates are object-oriented, type safe, and secure. Тип делегата задается его именем.The type of a delegate is defined by the name of the delegate. В следующем примере объявляется делегат с именем Del, который может инкапсулировать метод, использующий в качестве аргумента значение string и возвращающий значение void:The following example declares a delegate named Del that can encapsulate a method that takes a string as an argument and returns void:

public delegate void Del(string message);

Объект делегата обычно создается путем указания имени метода, для которого делегат будет служить оболочкой, или с помощью анонимной функции.A delegate object is normally constructed by providing the name of the method the delegate will wrap, or with an anonymous function. После создания экземпляра делегата вызов метода, выполненный в делегате передается делегатом в этот метод.Once a delegate is instantiated, a method call made to the delegate will be passed by the delegate to that method. Параметры, передаваемые делегату вызывающим объектом, передаются в метод, а возвращаемое методом значение (при его наличии) возвращается делегатом в вызывающий объект.The parameters passed to the delegate by the caller are passed to the method, and the return value, if any, from the method is returned to the caller by the delegate. Эта процедура называется вызовом делегата.This is known as invoking the delegate. Делегат, для которого создан экземпляр, можно вызвать, как если бы это был метод, для которого создается оболочка.An instantiated delegate can be invoked as if it were the wrapped method itself. Например:For example:

// Create a method for a delegate.
public static void DelegateMethod(string message)
{
    Console.WriteLine(message);
}
// Instantiate the delegate.
Del handler = DelegateMethod;

// Call the delegate.
handler("Hello World");

Типы делегатов являются производными от класса Delegate в платформа.NET Framework.Delegate types are derived from the Delegate class in the .NET Framework. Типы делегатов являются запечатанными — от них нельзя наследовать, а от Delegate нельзя создавать производные пользовательские классы.Delegate types are sealed—they cannot be derived from— and it is not possible to derive custom classes from Delegate. Поскольку созданный экземпляр делегата является объектом, его можно передавать как параметр или назначать свойству.Because the instantiated delegate is an object, it can be passed as a parameter, or assigned to a property. Это позволяет методу принимать делегат в качестве параметра и вызывать делегат в дальнейшем.This allows a method to accept a delegate as a parameter, and call the delegate at some later time. Эта процедура называется асинхронным обратным вызовом и обычно используется для уведомления вызывающего объекта о завершении длительной операции.This is known as an asynchronous callback, and is a common method of notifying a caller when a long process has completed. Когда делегат используется таким образом, коду, использующему делегат, не требуются сведения о реализации используемого метода.When a delegate is used in this fashion, the code using the delegate does not need any knowledge of the implementation of the method being used. Данные функциональные возможности аналогичны возможностям, предоставляемым интерфейсами инкапсуляции.The functionality is similar to the encapsulation interfaces provide.

Обратный вызов также часто используется для задания настраиваемого метода сравнения и передачи этого делегата в метод сортировки.Another common use of callbacks is defining a custom comparison method and passing that delegate to a sort method. Это позволяет сделать коду вызывающего объекта частью алгоритма сортировки.It allows the caller's code to become part of the sort algorithm. В следующем примере метод использует тип Del тип в качестве параметра.The following example method uses the Del type as a parameter:

public static void MethodWithCallback(int param1, int param2, Del callback)
{
    callback("The number is: " + (param1 + param2).ToString());
}

Затем в данный метод можно передать созданный ранее делегат:You can then pass the delegate created above to that method:

MethodWithCallback(1, 2, handler);

и получить следующие выходные данные в окне консоли:and receive the following output to the console:

The number is: 3

При использовании делегата в качестве абстракции методу MethodWithCallback не нужно выполнять непосредственный вызов консоли, то есть его можно создавать без учета консоли.Using the delegate as an abstraction, MethodWithCallback does not need to call the console directly—it does not have to be designed with a console in mind. Метод MethodWithCallback просто подготавливает строку и передает ее в другой метод.What MethodWithCallback does is simply prepare a string and pass the string to another method. Это очень удобно, так как делегируемый метод может использовать любое количество параметров.This is especially powerful since a delegated method can use any number of parameters.

Если делегат создан в качестве оболочки для метода экземпляра, этот делегат ссылается и на экземпляр, и на метод.When a delegate is constructed to wrap an instance method, the delegate references both the instance and the method. Делегат не имеет сведений о типе экземпляра, кроме полученных из метода, для которого он является оболочкой, поэтому делегат может ссылаться на любой тип объекта, если для этого объекта есть метод, соответствующий сигнатуре делегата.A delegate has no knowledge of the instance type aside from the method it wraps, so a delegate can refer to any type of object as long as there is a method on that object that matches the delegate signature. Если делегат создан в качестве оболочки для статического метода, он ссылается только на метод.When a delegate is constructed to wrap a static method, it only references the method. Рассмотрим следующее объявление:Consider the following declarations:

public class MethodClass
{
    public void Method1(string message) { }
    public void Method2(string message) { }
}

Вместе с рассмотренным ранее статическим методом DelegateMethod есть три метода, для которых можно создать оболочку с помощью экземпляра Del.Along with the static DelegateMethod shown previously, we now have three methods that can be wrapped by a Del instance.

При вызове делегат может вызывать сразу несколько методов.A delegate can call more than one method when invoked. Это называется многоадресностью.This is referred to as multicasting. Чтобы добавить в список методов делегата (список вызова) дополнительный метод, необходимо просто добавить два делегата с помощью оператора сложения или назначения сложения ("+" или "+=").To add an extra method to the delegate's list of methods—the invocation list—simply requires adding two delegates using the addition or addition assignment operators ('+' or '+='). Например:For example:

var obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;

//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;

На данном этапе список вызова делегата allMethodsDelegate содержит три метода — Method1, Method2 и DelegateMethod.At this point allMethodsDelegate contains three methods in its invocation list—Method1, Method2, and DelegateMethod. Три исходных делегата d1, d2 и d3 остаются без изменений.The original three delegates, d1, d2, and d3, remain unchanged. При вызове allMethodsDelegate все три метода вызываются по порядку.When allMethodsDelegate is invoked, all three methods are called in order. Если делегат использует параметры, передаваемые по ссылке, эта ссылка передается после каждого из трех методов, а все изменения одного из методов становятся видны в следующем методе.If the delegate uses reference parameters, the reference is passed sequentially to each of the three methods in turn, and any changes by one method are visible to the next method. Если любой из методов вызывает неперехваченное исключение, это исключение передается в вызывающий делегат объект, а последующие методы в списке вызова не вызываются.When any of the methods throws an exception that is not caught within the method, that exception is passed to the caller of the delegate and no subsequent methods in the invocation list are called. Если делегат имеет возвращаемое значение и (или) выходные параметры, он возвращает возвращаемое значение и параметры последнего вызванного метода.If the delegate has a return value and/or out parameters, it returns the return value and parameters of the last method invoked. Чтобы удалить метод из списка вызовов, используйте вычитание или операторы присваивания вычитания (- или -=).To remove a method from the invocation list, use the subtraction or subtraction assignment operators (- or -=). Например:For example:

//remove Method1
allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;

Поскольку типы делегата являются производными от System.Delegate, в делегате можно вызывать методы и свойства, определенные этим классом.Because delegate types are derived from System.Delegate, the methods and properties defined by that class can be called on the delegate. Например, чтобы определить число методов в списке вызова делегата, можно использовать код:For example, to find the number of methods in a delegate's invocation list, you may write:

int invocationCount = d1.GetInvocationList().GetLength(0);

Делегаты, в списке вызова которых находятся несколько методов, является производным от MulticastDelegate, являющегося подклассом класса System.Delegate.Delegates with more than one method in their invocation list derive from MulticastDelegate, which is a subclass of System.Delegate. Приведенный выше код работает в любом из случаев, так как оба класса поддерживают GetInvocationList.The above code works in either case because both classes support GetInvocationList.

Групповые делегаты часто используются при обработке событий.Multicast delegates are used extensively in event handling. Объекты источников событий отправляют уведомления объектам получателей, зарегистрированным для получения данного события.Event source objects send event notifications to recipient objects that have registered to receive that event. Чтобы зарегистрироваться для получения события, объект получателя создает метод, предназначенный для обработки этого события, затем создает делегат для этого метода и передает его в источник события.To register for an event, the recipient creates a method designed to handle the event, then creates a delegate for that method and passes the delegate to the event source. Когда происходит событие, источник вызывает делегат.The source calls the delegate when the event occurs. После этого делегат вызывает в объекте получателя обработки события, предоставив ему данные события.The delegate then calls the event handling method on the recipient, delivering the event data. Тип делегата для данного события задается источником события.The delegate type for a given event is defined by the event source. Дополнительные сведения см. в разделе События.For more, see Events.

Назначение сравнения делегатов двух различных типов во время компиляции вызовет ошибку компиляции.Comparing delegates of two different types assigned at compile-time will result in a compilation error. Если экземпляры делегата статически относятся к типу System.Delegate, сравнение допустимо, но во время выполнения будет возвращено значение false.If the delegate instances are statically of the type System.Delegate, then the comparison is allowed, but will return false at run time. Например:For example:

delegate void Delegate1();
delegate void Delegate2();

static void method(Delegate1 d, Delegate2 e, System.Delegate f)
{
    // Compile-time error.
    //Console.WriteLine(d == e);

    // OK at compile-time. False if the run-time type of f 
    // is not the same as that of d.
    Console.WriteLine(d == f);
}

См. такжеSee also