Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5636 / 790 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 9:

Делегаты и события

< Лекция 8 || Лекция 9: 12 || Лекция 10 >
Аннотация: Класс, структура, интерфейс, перечисление, делегат – это все разнообразные категории классов. Каждая категория имеет свои особенности объявления, свое назначение и строго определенную область применения. Об особенностях делегатов в данной лекции

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

Делегаты

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

Объявление класса делегата начинается ключевым словом 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.
< Лекция 8 || Лекция 9: 12 || Лекция 10 >
kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2