Делегаты и события
Класс, структура, интерфейс, перечисление, делегат – это все разнообразные категории классов. Каждая категория имеет свои особенности объявления, свое назначение и строго определенную область применения.
Делегаты
Объявление класса делегата начинается ключевым словом delegate и выглядит следующим образом:
ОбъявлениеКлассаДелегата ::= [СпецификаторДоступа] delegate СпецификаторВозвращаемогоЗначения ИмяКлассаДелегата (СписокПараметров);
При этом
СпецификаторВозвращаемогоЗначения ::= ИмяТипа ИмяКлассаДелегата ::= Идентификатор
а синтаксис элемента СписокПараметров аналогичен списку параметров функции. Но сначала – примеры объявления классов делегатов:
delegate int ClassDelegate(int key); delegate void XXX(int intKey, float fKey);
Подобие объявления класса-делегата и заголовка функции не случайно.
Класс-делегат способен порождать объекты. При этом назначение объекта — представителя класса-делегата заключается в представлении методов (функций-членов) РАЗЛИЧНЫХ классов. В частности, тех которые оказываются видимыми из пространства имен, содержащих объявление данного класса-делегата. Объект этого класса способен представлять ЛЮБЫЕ (статические и нестатические) функции — члены классов, лишь бы спецификация их возвращаемого значения и список параметров соответствовали бы характеристикам данного класса-делегата.
Любой класс-делегат наследует System.MulticastDelegate. Это обстоятельство определяет многоадресность делегатов: в ходе выполнения приложения объект-делегат способен запоминать ссылки на произвольное количество функций независимо от их статичности или нестатичности и принадлежности классам. Многоадресность обеспечивается внутренним списком, в который заносятся ссылки на функции, соответствующие заданной сигнатуре и спецификации возвращаемого значения.
Свойства и методы | Назначение |
---|---|
Method | Свойство. Возвращает имя метода, на который указывает делегат |
Target | Свойство. Возвращает имя класса, если делегат указывает на нестатический метод класса. Возвращает значение типа null, если делегат указывает на статический метод |
Combine(), operator+(), operator+=(), operator–(), operator–=() | Функция и операторные функции. Обеспечивают реализацию многоадресного делегата. Работа с операторными функциями смотрится как прибавление и вычитание ДЕЛЕГАТОВ. Арифметика делегатов |
GetInvocationList() | Основываясь на внутреннем списке ссылок на функции, строится соответствующий массив описателей типов функций. Попытка применения метода к пустому делегату приводит к возникновению исключения |
object DynamicInvoke (object[] args) | В соответствии со списком ссылок обеспечивается выполнение функций, на которые был настроен делегат |
static Remove() | Статический метод, обеспечивающий удаление элементов внутреннего списка ссылок на функции |
Пример объявления и применения делегата представлен ниже.
using System; namespace Delegates_1 { // Класс-делегат. Его объявление соответствует типу функции, // возвращающей значение int с одним параметром типа int. delegate int xDelegate(int key); //=========================================== class ASD { // Делегат как член класса. public xDelegate d; // А вот свойство, которое возвращает ссылку на делегат. public xDelegate D { get { return d; } } } //======================================================== // Класс, содержащий функцию-член, на которую может // быть настроен делегат - представитель класса xDelegate. //======================================================== class Q { public int QF(int key) { Console.WriteLine("int QF({0})", key); return key; } // А вот функция, в которой делегат используется как параметр! public void QQQ(int key, xDelegate par) { Console.WriteLine("Делегат как параметр!"); // И этот делегат используется по назначению – обеспечивает вызов // НЕКОТОРОЙ функции. Какой функции? А неизвестно какой! Той, // на которую был настроен делегат перед тем, как его ссылка была передана // в функцию QQQ. Здесь не интересуются тем, что за функция пришла. // Здесь запускается ЛЮБАЯ функция, на которую настраивался делегат. par(key); } } //=========================================== // Стартовый класс. Также содержит пару пригодных для настройки // делегата функций. class StartClass { // Одна статическая... public static int StartClassF0(int key) { Console.WriteLine("int StartClassF0({0})", key); return key; } // Вторая нестатическая... public int StartClassF1(int key) { Console.WriteLine("int StartClassF1({0})", key); return key; } // Процесс пошел! static void Main(string[] args) {//=============================================================== // Ссылка на делегат. xDelegate localDelegate; int i, n; // Объект – представитель класса StartClass. StartClass sc = new StartClass(); // Объект – представитель класса Q. Q q = new Q(); // Объект – представитель класса ASD. ASD asd = new ASD(); // Статическая функция – член класса – в списке делегата. // Поскольку делегат настраивается непосредственно в // классе, которому принадлежит данная статическая функция, // здесь обходимся без дополнительной спецификации имени // функции. asd.d = new xDelegate(StartClassF0); // Вот показали имя метода, на который настроен делегат. Console.WriteLine(asd.d.Method.ToString()); // Попытались показать имя класса – хозяина метода, на который // настроили делегат. Ничего не получится, поскольку метод – // статический. if (asd.d.Target != null) Console.WriteLine(asd.d.Target); // неСтатическая функция – член класса – в списке делегата. // добавляется к списку функций делегата посредством // операторной функции +=. asd.d += new xDelegate(sc.StartClassF1); Console.WriteLine(asd.d.Method.ToString()); if (asd.d.Target != null) Console.WriteLine(asd.d.Target); // Делегат также включил в список функцию – член класса Q. asd.d += new xDelegate(q.QF); Console.WriteLine(asd.d.Method.ToString()); if (asd.d.Target != null) Console.WriteLine(asd.d.Target); // Делегат разряжается последовательностью // вызовов разнообразных функций. // Либо так. asd.d(125); // Либо так. Параметр // при этом пришлось упаковать в одномерный массив типа object // длиной в 1. asd.d.DynamicInvoke(new object[]{0}); // Также есть возможность удаления функции из списка делегата. // Для этого воспользуемся локальным делегатом. localDelegate = new xDelegate(q.QF); asd.d –= localDelegate; asd.d(725); // А теперь опять добавим функцию в список делегата. asd.d += localDelegate; asd.d(325); Console.WriteLine(asd.d.Method.ToString()); // А теперь – деятельность по построению массива описателей типа ссылок. // Преобразование ОДНОГО объекта, содержащего массив ссылок на функции, // в массив объектов, содержащих по одной ссылке на функцию. // Таким образом, для каждой ссылки на функцию делегата в рамках // массива делегатов строится свой собственный делегат. // Естественно что элемент массива уже не является Multicast'овым // делегатом. Поэтому с его помощью можно выполнить ОДНУ функцию // Multicast'ового делегата. Console.WriteLine("—In array!––––––––––––––––––––––––– "); // Вот ссылка на массив делегатов. // Сюда будем сгружать содержимое Multicast'а. xDelegate[] dlgArray; try { dlgArray = asd.d.GetInvocationList(); } catch (System.NullReferenceException e) { Console.WriteLine(e.Message + ":Delegate is empty"); return; } // Прочитали содержимое массива описателей типа ссылок. for (i = 0, n = dlgArray.Length; i < n; i++) { Console.WriteLine(dlgArray[i].Method.ToString()); // А можно и так, без помощи метода ToString()... С тем же результатом. //Console.WriteLine(dlgArray[i].Method); } // Вот как запускается ОДНА функция из списка ссылок на функции! // Используются разные варианты запуска. Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); dlgArray[1].DynamicInvoke(new object[]{75}); ((xDelegate)(dlgArray[2]))(123); Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); // Используем этот массив для преобразования массива ссылок делегата. // Выкидываем из массива один элемент. // Возвращаемое значение (НОВЫЙ делегат с измененным списком) // может быть "поймано" и другим делегатом! asd.d = (xDelegate)xDelegate.Remove(asd.d,dlgArray[1]); // Таблица ссылок модифицированного делегата сократилась! Console.WriteLine("Таблица ссылок сократилась! "); asd.d(125); // Через свойство получили ссылку на делегат. xDelegate dFROMasd = asd.D; // Таблица ссылок опустошается! // Еще бы! Здесь из списка методов одного делегата // удаляются ВСЕ делегаты, которые встречаются в списке // методов второго делегата. Буквально: удали из этого списка // ВСЕ методы, которые встретишь в этом списке! // Что-то из жизни того парня, который вытягивал сам себя // за волосы из болота! И если бы НОВЫЙ делегат не был бы // перехвачен на другую ссылку – весь исходный список // методов Multicast'ового делегата asd.d был бы потерян. dFROMasd = (xDelegate)xDelegate.RemoveAll(asd.d,asd.d); // В чем и можно убедиться вот таким способом! try { dlgArray = dFROMasd.GetInvocationList(); } catch (System.NullReferenceException e) { Console.WriteLine(e.Message + ":Delegate is empty"); } // Но только не исходный делегат! Console.WriteLine("Но только не исходный делегат!"); // В чем и можно убедиться вот таким способом! try { dlgArray = asd.d.GetInvocationList(); } catch (System.NullReferenceException e) { Console.WriteLine(e.Message + ":Delegate is empty"); } // Вот! Исходный объект класса-делегата – не пустой! asd.d(125); q.QQQ(75,asd.d); }//============================================================== } }Листинг 9.1.