Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Операции над делегатами. Класс Delegate






При определении функционального типа, например:

public delegate int FType(int X);

переменная FType принадлежит классу Delegate. Почему же ее нельзя объявить привычным образом? Дело не только в синтаксических особенностях этого класса. Дело в том, что класс Delegate является абстрактным классом. Вот его объявление:

public abstract class Delegate: ICloneable, ISerializable

Для абстрактных классов реализация не определена, и это означает, что нельзя создавать экземпляры класса. Класс Delegate служит базовым классом для классов-наследников. Но создавать наследников могут только компиляторы и системные программы, этого нельзя сделать в программе на C#. Именно поэтому введено ключевое слово delegate, которое косвенно позволяет работать с классом Delegate, создавая уже не абстрактный, а реальный класс.

Если функции, выполняющие отдельные работы, принадлежат одному классу, то для решения задачи можно использовать технику их комбинирования.

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

К экземпляру делегату разрешается поочередно присоединять другие экземпляры делегата того же типа. Поскольку каждый экземпляр хранит ссылку на функцию, в результате создается список ссылок на функции. Этот список называется списком вызовов (invocation list). Когда вызывается экземпляр с присоединенным списком вызова, поочередно, в порядке присоединения, начинают вызываться и выполняться функции, заданные ссылками. Так один вызов порождает выполнение списка работ.

Каждый делегат в.NET Framework (включая специальные делегаты) автоматически снабжается способностью вызывать свои методы синхронно или асинхронно. Этот факт значительно упрощает задачи программирования, поскольку позволяет вызывать метод во вторичном потоке выполнения без ручного создания и управления объектом Thread.

Рассмотрим основные методы и свойства класса Delegate. Начнем с двух статических методов - Combine и Remove. Первый из них присоединяет экземпляры делегата к списку, второй - удаляет из списка. Оба метода имеют похожий синтаксис:

Combine(del1, del2)Remove(del1, del2)

Аргументы del1 и del2 должны быть одного функционального класса. При добавлении del2 в список, в котором del2 уже присутствует, будет добавлен второй экземпляр. При попытке удаления del2 из списка, в котором del2 нет, Remove благополучно завершит работу, не выдавая сообщения об ошибке.

Класс Delegate относится к неизменяемым классам, поэтому оба метода возвращают ссылку на нового делегата. Возвращаемая ссылка принадлежит родительскому классу Delegate, поэтому ее необходимо явно преобразовать к нужному типу, которому принадлежат del1 и del2. Обычное использование этих методов имеет вид:

del1 = (< type>) Combine(del1, del2); del1 = (< type>) Remove(del1, del2);

Метод GetInvocationList является динамическим методом класса - он возвращает список вызовов экземпляра, вызвавшего метод. Затем можно устроить цикл foreach, поочередно получая элементы списка.

Каждый создаваемый делегат определяет три общедоступных метода. Invoke() — возможно, главный из них, поскольку он используется для синхронного вызова каждого из методов, поддерживаемых объектом делегата; это означает, что вызывающий код должен ожидать завершения вызова, прежде чем продолжить свою работу. Может показаться странным, что синхронный метод Invoke() не должен вызываться явно в коде С#. Invoke() вызывается " за кулисами", когда применяется соответствующий синтаксис С#.

Методы BeginInvoke() и EndInvoke() предлагают возможность вызова текущего метода асинхронным образом, в отдельном потоке выполнения. Имеющим опыт в многопоточной разработке должно быть известно, что одной из основных причин, вынуждающих разработчиков создавать вторичные потоки выполнения, является необходимость вызова методов, которые требуют определенного времени для завершения. Хотя в библиотеках базовых классов.NET предусмотрено целое пространство имен, посвященное многопоточному программированию (System.Threading), делегаты предлагают эту функциональность в готовом виде.

 

Операции " +" и " -"

Наряду с методами, над делегатами определены и две операции: " +" и " -", которые являются более простой формой записи добавления в список вызовов и удаления из списка. Операции заменяют собой методы Combine и Remove. Выше написанные присваивания объекту del1 с помощью этих операций могут быть переписаны в виде:

del1 +=del2; del1 -=del2;

 

Запись становится проще, исчезает необходимость в задании явного приведения к типу. Ограничения на del1 иdel2, естественно, остаются те же, что и для методов Combine и Remove.

Заметим, что если методы из списка являются функциями, возвращающими значение, то в результате работы все значения, кроме последнего, будут утеряны и на печать будет послано сообщение о результатах работы последней отработавшей функции. Также, если очередной вызов приведет к выбрасыванию исключительной ситуации, тогда стоящие за ним в очереди экземпляры не будут вызваны. В этом случае полезно использовать метод GetInvocationList и самому клиенту организовать в цикле поочередный вызов сервисов из списка. Вызов метода следует поместить в охраняемый блок, тогда при возникновении исключительной ситуации в обработчике ситуации можно получить и выдать пользователю всю информацию о нарушителе, а цикл продолжит выполнение очередных методов из списка вызова.

foreach(DelegateType currentJob in Comb.GetInvocationList()) { try { Console.WriteLine(currentJob()); } catch(Exception e) { Console.WriteLine(e.Message); Console.WriteLine(currentJob.Method.Name); } }

Ковариантность и контравариантность делегатов

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

Пример.

class Parent { }

class Derived: Parent { }

delegate Parent Der(Derived p);

public Derived PD(Parent p)//Ковариантность и контравариантность делегатов - возвращает не Parent, а потомка; аргумент не Derived, а родитель

{

return null;

}

Делегаты Action< T> и Func< T>

Вместо определения нового типа делегата с каждым типом параметра и возврата можно использовать делегаты Action< T> и Func< T>. Обобщенный делегат Action< T> предназначен для ссылки на метод, возвращающий void. Этот класс делегата существует в различных вариантах, так что ему можно передавать до 16 разных типов параметров.

Класс Action без обобщенного параметра предназначен для вызова методов без параметров, Action< in Т> — для вызова метода с одним параметром, Action< in T1, in Т2> — для вызова метода с двумя параметрами и Action< in T1, in Т2, in ТЗ, in Т4, in Т5, in Т6, in Т7, in Т8> — для вызова метода с восемью параметрами.

Делегаты Func< T> могут использоваться аналогичным образом. Func< T> позволяет вызывать методы с типом возврата. Подобно Action< T>, Func< T> определен в разных вариантах для передачи до 16 типов параметров и типа возврата. Func< out Tresult> — тип делегата для вызова метода с типом возврата, но без параметров, Func< in T1, out TResult> — для метода с одним параметром, a Func< in T1, in T2, in ТЗ, in T4, out TResult> — для метода с четырьмя параметрами.


Пример.

Func< int, int, bool> c1 = ((x, y) => x > y + 10);







© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.