Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
4 сем Инфа готово.docx
Скачиваний:
24
Добавлен:
04.06.2015
Размер:
255.66 Кб
Скачать

Вопрос 10-11. Делегаты

Делегаты являются контейнерными объектами, хранящими в себе указатель на объект и данные о сигнатуре метода. Делегат указывает на метод объекта, но скрывает информацию о том, какой именно это метод, предоставляя только сигнатуру метода. Делегаты и для методов экземпляров и для стататических методов класса создаются как производные от класса System.Delegate.

Возможно создание комбинированных делегатов путем объединения двух или более объединяемых делегатов. При вызове такого делегата, производится вызов всех входящих в его состав делегатов в порядке их объявления.

События реализуют механизм уведомления одним классом других классов об определенных событиях в этом классе. Наиболее интенсивно события используются при разработке оконного пользовательского интерфейса. Все подписчики события, установившие с ним связь, получают уведомления (аналогично тому, как это происходит при использовании комбинированных делегатов)

Все мы помним об указателях на функцию в C/C++, пример для тех кто забыл:

int (*somefunc)(int param1, int param2);

Здесь мы объявили указатель на функцию, которая принимает два парамера типа int, и возвращает целое число int.

Перейдём к C#.

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

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

Делегат объявляется с помощью ключевого слова delegate. Общая форма объявления делегата имеет следующий вид:delegate тип_возврата имя(список_параметров);

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

Создайте консольный проект, вставьте в функцию Main объявление

Delegate_Test D = new Delegate_Test(); и посмотрите, что будет.

class Delegate_Test

{

int MyTrunc(double param)

{

return (int)param;

}

int Another(double par)

{

return 0;

}

delegate int new_delgate_type(double some_param);

new_delgate_type delegate_object1;

double d_data;

int i_data;

public Delegate_Test()

{

d_data = 42.23;

delegate_object1 = new new_delgate_type(MyTrunc); // делегат - это объект, и мы обязаны

//передать его конструктору имя функции, на которую он будет ссылаться

i_data = delegate_object1(d_data); //компилятор не знает о том, куда на самом деле ссылается делегат

//принятие решения о вызове функции MyTrunc принимается при исполнении, читай - полиморфизм.

Console.WriteLine(i_data);

delegate_object1 = new new_delgate_type(MyFloat); // мы присвоили объекту-делегату адрес совсем другой функции

i_data = delegate_object1(d_data);

Console.WriteLine(i_data);

}

}

Одна из самых интересных возможностей делегата — поддержка многоадресатной передачи (multicasting). Выражаясь простым языком, Многоадресатная передача — это способность создавать список вызовов (или цепочку вызовов) методов, которые должны автоматически вызываться при вызове делегата. Такую цепочку создать нетрудно.

Достаточно создать экземпляр делегата, а затем для добавления методов в эту цепочку использовать оператор "+=". Для удаления метода из цепочки используется оператор

"-= " . (Можно также для добавления и удаления методов использовать в отдельности операторы "+", " - " и "=", но чаще применяются составные операторы "+=" и "-=").

Делегат с многоадресатной передачей имеет одно ограничение: он должен возвращать тип void.

11.На основе делегатов построено еще одно важное средство С#: событие (event). Событие — это по сути автоматическое уведомление о выполнении некоторого действия. События работают следующим образом. Объект, которому необходима информация о некотором событии, регистрирует обработчик для этого события. Когда ожидаемое событие происходит, вызываются все зарегистрированные обработчики. А теперь внимание: обработчики событий представляются делегатами. События — это члены класса, которые объявляются с использованием ключевого слова event. Наиболее распространенная форма объявления события имеет следующий вид: event событийный_делегат объект;

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

Рассмотрим пример.

// Демонстрация использования простейшего события.

using System;

// Объявляем делегат для события,

delegate void MyEventHandler();

// Объявляем класс события,

class MyEvent

{

public event MyEventHandler SomeEvent;

// Этот метод вызывается для генерирования события,

public void OnSomeEvent()

{

if(SomeEvent != null)

SomeEvent();

}

}

class EventDemo

{

// Обработчик события,

static void handler()

{

Console.WriteLine("Произошло событие.");

}

public static void Main()

{

MyEvent evt = new MyEvent();

// Добавляем метод handler() в список события,

evt.SomeEvent += new MyEventHandler(handler);

// Генерируем событие,

evt.OnSomeEvent();

}

}

// конец

При выполнении программа отображает следующие результаты:

Произошло событие.

Все события активизируются посредством делегата. Следовательно, событийный делегат определяет сигнатуру для события. В данном случае параметры отсутствуют, однако событийные параметры разрешены. Поскольку события обычно предназначены для многоадресатной передачи, они должны возвращать значение типа void.

Обратите внимание на то, что обработчик события вызывается только в том случае, если делегат SomeEvent не равен null-значению. Поскольку другие части программы, чтобы получить уведомлении о событии, должны зарегистрироваться, можно сделать так, чтобы метод OnSomeEvent () был вызван до регистрации любого обработчика события. Чтобы предотвратить вызов null-объекта, событийный делегат необходимо протестировать и убедиться в том, что он не равен пи 11-значению. Внутри класса EventDemo создается обработчик события handler(). В этом примере обработчик события просто отображает сообщение, но ясно, что другие обработчики могли бы выполнять более полезные действия.

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