- •Конспект лекций по курсу «Сети эвм» Часть 2 Сурков д.А.
- •Сравнительная характеристика технологий .Net и Java
- •Общее для Java и .Net.
- •Управление памятью в .Net
- •Процедурные типы данных (делегаты)
- •Динамические массивы
- •Многозадачность
- •1: Добавление в очередь ThreadPool нового делегата, который будет запускаться в отдельном потоке (из пула свободного потока). Делегат отработал – поток обратно в пул.
- •Исключительные ситуации
- •Шаблоны в с# (появились недавно в .Net 2.0)
- •В с# в шаблонах введено понятие итератора.
- •Массивы
- •Метод Get в SystemArray
- •Средства удалённого вызова .Net Remoting
- •Пользовательские атрибуты
- •Inherited – будем ли создавать атрибут для наследника.
- •Защищённые информационные системы. Технология “Эльбрус”
- •Защищенная файловая система
Процедурные типы данных (делегаты)
Процедурные типы данных позволяют разработать программу с так называемыми “обратными связями”. Делегаты необходимы в программах в следующих случаях: когда необходимо предоставить возможность третьим разработчикам расширять функциональность программы без какого-либо изменения готовых модулей (без их перекомпиляции). Например, компонент Button имеет делегата (процедурный тип) – click.
Пусть необходимо связать какие-то действия с каким-то нажатием на кнопку. Делегаты позволяют запланированно вставить в код вызов неизвестного на этапе компиляции метода (т.е. адрес которого не известен на этапе компиляции программы). Делегаты позволяют организовать анонимные связи в программе, когда имя процедуры в момент вызова неизвестен:
void A( )
{
(* B) ( ); // на C++, B хранит указатель на процедуру
}
Делегат – это указатель на процедуру, но физически делегат – это два указателя:
1-ый – указатель на метод (процедуру) объекта, 2-ой – указатель на объект, чей метод хранится в первом указателе.
Пример: процедурный тип объявляется следующим образом (на Delphi)
type
ClickProc = procedure (Position:Point); // объявление процедурного типа данных
var
Click:ClickProc; // объявление перменной
type
Button = class
Click:ClickProc; // физически это поле - двойной указатель(изначально null)
…
end;
Form = class // класс Form
…
procedure ButtonClick (Position:Point); // процедура по списку совпадает с
// процедурным типом ClickProc
end;
var
B:Button;
F:Form;
begin
F:=Form.Create;
B:=Button.Create;
…
B.Click=F.ButtonClick; // положить в поле Click в Method адрес метода
// ButtonClick, берется адрес объекта F и кладется в поле
// Object
…
if Assigned (B.Click) then
B.Click (…); // обращение к полю с вызовом метода
Указатель на метод может указывать на статический метод (метод, который оперирует не объектом, а является глобальными процедурами). Если переменная процедурного типа указывает на глобальную процедуру, то в поле Object этой переменной вписывается null в качестве значения. Код вызова метода всегда имеет следующую логику: сначала проверяется, если значение подполя Object не null, то в стек дополнительно помещается указатель на объект, если = null, то в стек не помещается.
В C++ нет раздела объявления типов. Поэтому ввели раздел delegate:
public delegate void ClickProc (Point Position);
ClickProc Click = new ClickProc (F.ButtonClick); // это поле необходимо создавать
// явно
В .Net есть возможность многоадресового вызова, делегаты могут иметь много получателей (процедур, которые вызываются). Физически делегаты .Net бывают простые (как в примере с Delphi) это объект стандартного класса Delegate, а есть делегаты стандартного класса MultiCastDelegate (добавляются дополнительные поля).
MultiCastDelegate физически имеет следующую структуру:
Prev – это ссылка на предыдущего делегата, т.е. делегаты могут собираться в список. Вызов такого делегата не отличается от вызова обычного делегата, но за ним стоит вызов по списку всех процедур, которые были добавлены в этот список. Это означает, что вместо оператора присваивания значения процедурной переменной необходим оператор включения метода объекта в список делегатов.
Включение новой процедуры (нового метода):
Вновь добавленный делегат – в списке первый. Вызов делегатов по цепочке – рекурсивный. Вызывается специальная процедура, которой передается _target, _methodPtr, _prev. Это процедура вызывает себя:
void CallDelegate (void *target, void *method, void *prev)
{
if ( prev != null)
CallDelegate (prev -> target, prev -> method, prev -> prev …);
(target -> *method) (…);
}
параметры метода
Т.о., первым будет вызван самый последний (тот, кто был добавлен первым):
Рекурсия необходима для решения проблемы удаления делегатов из цепочки делегатов. Это решение является безопасным в многопоточных программах. При рекурсивном вызове в стеке временно создается полный дубликат списка делегатов. Поэтому удаление делегата в момент работы любого делегата никак не влияет на порядок их вызова и никак не конфликтует с вызовом делегатов.
Если делегат удаляется из списка во время выполнения, то он все равно будет вызван.
Копия списка помещается в стек – сборщик мусора не удалит делегат, пока он работает (ссылка на запись есть в стеке, следовательно, область памяти делегата еще достижима).
event – это MultiCastDelegate, для которого определены операторы добавления и удаления в список, из списка делегатов (+=, -=).