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

Делегаты

Класс, структура, интерфейс, перечисление, делегат – это всё разнообразные категории классов. Каждая категория имеет свои особенности объявления, своё назначение и строго определённую область применения.

Делегат – это тоже класс.

Объявление класса делегата начинается ключевым словом delegate и выглядит следующим образом:

ОбъявлениеКлассаДелегата ::=

[СпецификаторДоступа]

delegate

СпецификаторВозвращаемогоЗначения

ИмяКлассаДелегата

(СписокПараметров);

При этом

СпецификаторВозвращаемогоЗначения ::= ИмяТипа

ИмяКлассаДелегата ::= Идентификатор

а синтаксис элемента СписокПараметров аналогичен списку параметров функции. Но сначала примеры объявления классов делегатов.

delegate int ClassDelegate(int key);

delegate void XXX(int intKey, float fKey);

Подобие объявления класса делегата и заголовка функции не случайно.

Класс делегат способен порождать объекты. При этом назначение объекта-представителя класса-делегата заключается в представлении методов (функций-членов) РАЗЛИЧНЫХ классов.

Тех классов, которые оказываются видимыми из пространства имён, содержащих объявление данного класса-делегата. Объект этого класса способен представлять ЛЮБЫЕ (статические и нестатические) функции-члены классов, лишь бы спецификация их возвращаемого значения и список параметров соответствовали бы характеристикам данного класса-делегата.

В основе любой класс-делегат является многоадресным (multicast delegate). Это означает, что в ходе выполнения приложения делегат способен запоминать ссылки на произвольное количество функций-членов. Эта многоадресность обеспечивается внутренним списком, в который и заносятся ссылки на разнообразные функции-члены, соответствующие заданной сигнатуре и спецификации возвращаемого значения.

Класс-делегат является производным от 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);

// Вот показали имя метода, на который настроен делегат.

// Попытались показать имя класса – хозяина метода, на который

// настроили делегат. Ничего не получится, поскольку метод

// статический.

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’а.

Delegate[] 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);

}//===============================================================

}

}