Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП.doc
Скачиваний:
1
Добавлен:
24.09.2019
Размер:
522.24 Кб
Скачать

Вопрос 17) Делегаты в классах с#

Делегат (англ. delegates) — безопасный указатель на функцию. Безопасный в том плане, что строго указаны тип возвращаемого значения и аргументы метода (сигнатура). Делегаты введены в C# и других языках технологии Microsoft .NET реализованной в виде платформы .NET Framework для определения прототипа функции обратного вызова. Делегаты, так же, можно назвать псевдонимом сигнатур методов.

Из объявления типа делегата компилятор генерирует класс, производный от System.MulticastDelegate. Таким образом, сигнатура функции, принимающей делегат в качестве аргумента, может выглядеть так:

public MyFunction (Delegate anotherFunction);

Дополнительной особенностью делегатов является то, что их можно вызывать асинхронно асинхронный делегат, с помощью метода BeginInvoke(). В этом случае в пуле трэдов(анг. Thread) ищется свободный и указанная функция выполняется параллельно в его контексте. Стоит однако отметить, что количество потоков в пуле ограничено (в текущей реализации .NET их 25), и остальные вызовы будут ждать своей очереди.

Пример объявления и использования делегата

using System;

// Объявление делегата

delegate void MyDelegate(string a);

class DelegateExample

{

static void Func(string param)

{

System.Console.WriteLine("Вызвана функция с параметром {0}.", param);

}

public static void Main()

{

// Создание экземпляра делегата

MyDelegate f = new MyDelegate(Func);

// Вызов функции

f("hello");

}

}

Делегат — эти тип, который безопасно инкапсулирует метод, т. е. его действие схоже с указателем функции в C и C++.В отличие от указателей функций в C делегаты объектно-ориентированы, строго типизированы и безопасны.Тип делегата задается его именем.В следующем примере объявляется делегат с именем Del, который может инкапсулировать метод, использующий в качестве аргумента значение string и возвращающий значение void:

VB

C#

C++

F#

JScript

public delegate void Del(string message);

VB

C#

C++

F#

JScript

public delegate void Del(string message);

Объект делегата обычно создается указанием имени метода, для которого делегат будет служить оболочкой, или с помощью анонимного метода.После создания экземпляра делегата вызов метода, выполненный в делегате, передается делегатом в этот метод.Параметры, передаваемые делегату вызывающим объектом, передаются в метод, а возвращаемое методом значение (при его наличии) возвращается делегатом в вызывающий объект.Эта процедура называется вызовом делегата.Делегат, для которого создан экземпляр, можно вызвать, как если бы это был метод, для которого создается оболочка.Пример.

VB

C#

C++

F#

JScript

// Create a method for a delegate.

public static void DelegateMethod(string message)

{

System.Console.WriteLine(message);

}

VB

C#

C++

F#

JScript

// Create a method for a delegate.

public static void DelegateMethod(string message)

{

System.Console.WriteLine(message);

}

VB

C#

C++

F#

JScript

// Instantiate the delegate.

Del handler = DelegateMethod;

// Call the delegate.

handler("Hello World");

VB

C#

C++

F#

JScript

// Instantiate the delegate.

Del handler = DelegateMethod;

// Call the delegate.

handler("Hello World");

Типы делегатов являются производными от класса Delegate в платформе .NET Framework.Типы делегатов являются запечатанными — от них нельзя наследовать, а от Delegate нельзя создавать производные пользовательские классы.Поскольку созданный экземпляр делегата является объектом, его можно передавать как параметр или назначать свойству.Это позволяет методу принимать делегат в качестве параметра и вызывать делегат впоследствии.Эта процедура называется асинхронным обратным вызовом и обычно используется для уведомления вызывающего объекта о завершении длительной операции.Когда делегат используется таким образом, коду, использующему делегат, не требуются сведения о реализации используемого метода.Данные функциональные возможности похожи на возможности, предоставляемые интерфейсами инкапсуляции.Дополнительные сведения см. в разделе Использование делегатов вместо интерфейсов.

Обратный вызов также часто используется для задания специального метода сравнения и передачи этого делегата в метод сортировки.Это позволяет сделать код вызывающего объекта частью алгоритма сортировки.В следующем примере метод использует тип Del как параметр:

VB

C#

C++

F#

JScript

public void MethodWithCallback(int param1, int param2, Del callback)

{

callback("The number is: " + (param1 + param2).ToString());

}

VB

C#

C++

F#

JScript

public void MethodWithCallback(int param1, int param2, Del callback)

{

callback("The number is: " + (param1 + param2).ToString());

}

Затем можно передать созданный ранее делегат в данный метод:

VB

C#

C++

F#

JScript

MethodWithCallback(1, 2, handler);

VB

C#

C++

F#

JScript

MethodWithCallback(1, 2, handler);

и получить следующие выходные данные в окне консоли:

The number is: 3

При использовании делегата в качестве абстракции методу MethodWithCallback не нужно выполнять непосредственный вызов консоли, то есть его можно создавать без учета консоли.Метод MethodWithCallback просто подготавливает строку и передает ее в другой метод.Это очень удобно, так как делегируемый метод может использовать любое количество параметров.

Если делегат создан для обеспечения оболочки для метода экземпляра, то этот делегат ссылается и на экземпляр, и на метод.Делегат не имеет сведений о типе экземпляра, кроме полученных из метода, для которого он обеспечивает оболочку, поэтому делегат может ссылаться на любой тип объекта, если для этого объекта есть метод, соответствующий сигнатуре делегата.Если делегат создан для обеспечения оболочки для статического метода, то этот делегат ссылается только на метод.Рассмотрим следующие объявления:

VB

C#

C++

F#

JScript

public class MethodClass

{

public void Method1(string message) { }

public void Method2(string message) { }

}

VB

C#

C++

F#

JScript

public class MethodClass

{

public void Method1(string message) { }

public void Method2(string message) { }

}

Вместе с рассмотренным ранее статическим методом DelegateMethod у нас есть три метода, для которых можно создать оболочку с помощью экземпляра Del.

При вызове делегат может вызывать сразу несколько методов.Это называется многоадресностью.Чтобы добавить в список методов делегата (список вызова) дополнительный метод, необходимо просто добавить два делегата с помощью оператора сложения или назначения сложения ("+" или "+=").Пример.

VB

C#

C++

F#

JScript

MethodClass 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;

VB

C#

C++

F#

JScript

MethodClass 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.Три исходных делегата d1, d2 и d3 остаются без изменений.При вызове allMethodsDelegate все три метода вызываются по порядку.Если делегат использует параметр, передаваемый по ссылке, эта ссылка передается после каждого из трех методов, а все изменения одного из методов становятся видны в следующем методе.Если любой из методов вызывает неперехваченное исключение, то это исключение передается в вызывающий делегат объект, а последующие методы в списке вызова не вызываются.Если делегат имеет возвращаемое значение и/или выходные параметры, он возвращает возвращаемое значение и параметры последнего вызванного метода.Для удаления метода из списка вызова используйте оператор декремента или назначения декремента ("-" или "-=").Пример.

VB

C#

C++

F#

JScript

//remove Method1

allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2

Del oneMethodDelegate = allMethodsDelegate - d2;

VB

C#

C++

F#

JScript

//remove Method1

allMethodsDelegate -= d1;

// copy AllMethodsDelegate while removing d2

Del oneMethodDelegate = allMethodsDelegate - d2;

Поскольку типы делегата являются производными от System.Delegate, в делегате можно вызывать методы и свойства, задаваемые данным классом.Например, чтобы определить число методов в списке вызова делегата, можно использовать код:

VB

C#

C++

F#

JScript

Копировать

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

VB

C#

C++

F#

JScript

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

Делегаты, в списке вызова которых находятся несколько методов, является производным от MulticastDelegate, являющегося подклассом System.Delegate.Приведенный выше код работает в любом из случаев, так как оба класса поддерживают GetInvocationList.

Групповые делегаты часто используются при обработке событий.Объекты источников событий отправляют уведомления объектам получателей, зарегистрированным для получения данного события.Чтобы зарегистрироваться для получения события, объект получателя создает метод, предназначенный для обработки этого события, затем создает делегат для этого метода и передает его в источник события.Когда происходит событие, источник вызывает делегат.После этого делегат вызывает в объекте получателя метод обработки события, предоставив ему данные события.Тип делегата для данного события задается источником события.Дополнительные сведения см. в разделе События (Руководство по программированию в C#).

Назначение сравнения делегатов двух различных типов во время компиляции вызовет ошибку компиляции.Если экземпляры делегата статически относятся к типу System.Delegate, то сравнение допустимо, но возвратит значение false во время выполнения.Пример.

VB

C#

C++

F#

Jscript

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.

System.Console.WriteLine(d == f);

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]