Delegate[] dd = d.GetInvocationList(); // Просим дать список заданий
Console.WriteLine ("\n\nGo through the array of delegates and plus a delegate:\n"); Test test = new Test (); // Объект нужен, т.к. Main — static
for (int i=0; i<dd.Length; i++, Console.WriteLine ()) // В массиве два делегата
{
MyDelegateType dt = dd[i] as MyDelegateType; // Приводим тип, так как в массиве объекты типа Delegate
dt += new MyDelegateType (test.NewTask); // Делегат + делегат = сложный делегат
dt ("Hi,");
}
}
}
Запустите приложение и убедитесь, что вам понятна логика его работы. Пожалуйста, прочтите все комментарии, они не тривиальны. Вызов d.GetInvocationList() можно трактовать, как способ выудить у одного делегата d список его заданий. Этот список представлен в виде двух делегатов, у каждого из которых по одному заданию. Обратите внимание на правила делегатной арифметики. Одно из правил можно трактовать так:
Делегат += делегат = более сложный делегат; // А не два делегата, как было бы естественно предположить
Важно понять, что операции += и –= переопределены так, что они способны добавлять и вычитать адреса вызываемых функций из списка заданий делегата. Вы можете продолжить исследование, вставив такой фрагмент:
Console.WriteLine ("\nOne relieved delegate:\n");
d -= d1;
d("Relieved, Hi ");
Класс Delegate является базовым для всех делегатных типов данных. Мы можем пользоваться его статическими методами, но не можем производить свои классы, наследующие от него. Только система и компилятор пользуется этим классом для создания производных классов. Но мы можем создать свой собственный делегатный тип — сигнатуру типового задания. Для этого надо использовать ключевое слово delegate. Оно введено в язык С# специально для этой цели.
Возвратимся к классу Man. Предположим, что мы хотим вывести содержимое списка людей в консольное окно. Действия такого рода удобны для демонстрации делегатов. В качестве методов, которые будут вызываться делегатами, используем два метода: Surname и Firstname. Первый выводит имя человека, а второй фамилию. Например, если name = "Alex Black", то FirstName должен вывести на экран строку Alex, а Surname — Black.
public void Firstname ()
{
int n = name.IndexOf(' ');
if (n < 0)
n = name.Length;
Console.WriteLine("{0}", name.Substring (0, n));
}
public void Surname ()
{
Console.WriteLine("{0}", name.Substring (name.LastIndexOf(' ') + 1) );
}
Важно, чтобы оба метода имели одинаковую сигнатуру (прототип). В нашем случае это требование удовлетворяется: обе функции имеют тип void, то есть ничего не возвращают в точку вызова и обе не требуют параметров. На самом деле, вы помните, что при вызове обычного метода один неявно передаваемый параметр есть всегда. Он представляет собой ссылку this. Эта ссылка может указывать на объект производного класса (Stud или Prof), но полиморфизм, реализованный в рамках С#, допускает такую подмену.
После выбора функций, которые будут приписаны делегату, надо объявить делегатный тип в том классе, в котором он будет использоваться. Поместите это объявление в класс ManTest.
public delegate void ManDelegate();
Оно создает новый тип данных — ManDelegate. Объекты этого типа будут созданы позже. Такое объявление задает сигнатуру функции обработки и, в то же время определяет делегатный класс, реализация которого скрыта от нас. Ее берет на себя CLR. Все переменные типа ManDelegate — это объекты класса, умеющего работать с указателями на функции с указанной сигнатурой. Имея такой класс, мы можем создавать объекты, инициализировать их адресом той или иной функции (метода класса) и использовать их для запуска этой функции. В нашем случае этими функциями будут методы Firstname и Surname. Если вам хочется быть более точным, то замените все вхождения слов «указатель» и «адрес» на слово «ссылка». Для тех же, кто привык работать с адресами, истинная сущность происходящего становится более понятной, если использовать понятия указатель и адрес. Добавьте в класс ManTest объявление коллекции людей:
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.