}
Часто объявление указателя производится в два этапа: сначала объявляют тип указателей, способных хранить адреса функций с определенным прототипом, затем объявляют переменную этого типа. Например:
typedef double (*pTrig) (double);
В этом случае работать с указателем становится проще:
void main()
{
pTrig p = sin;
cout << "sin (1.57) = " << p (1.57)<<"\n\n";
p = tan;
cout << "tan (0.78) = " << p (0.78)<<"\n\n";
p = Sum;
cout << "Sum (1e-5) = " << p (1e-5)<<"\n\n";
}
Вернемся в C#. Делегат в С# — это понятие более сложное, чем указатель на функции и даже чем тип указателей на функции. С его помощью можно не только вызвать одну из функций с определенным прототипом, но и сразу запустить цепочку вызовов, то есть последовательно вызвать несколько функций. Это похоже на то, что мы сначала создаем умного (способного запоминать адреса функций) делегата, даем ему список поручений, а затем отправляем на работу. Каждый делегат имеет список заданий (Invocation List). При запуске делегата, он отрабатывает свой список, запуская методы, адреса которых находятся в списке (массиве) заданий. В качестве методов, приписываемых делегату в качестве заданий, могут выступать только те, которые соответствуют его сигнатуре. Она, как было сказано определяется делегатным типом.
Несмотря на то, что адреса и указатели используются в С# лишь неявно, все-таки удобно интерпретировать этот новый тип следующим образом. Все переменные типа Delegate — это объекты класса, умеющего работать с указателями на функции с указанной сигнатурой. Имея такой класс, мы можем создавать объекты, инициализировать их адресом (или адресами) того или иного метода класса и использовать их для их запуска. Итак в консольном пректе Visual C# определим сначала какой-либо делегатный тип, например:
using System;
public delegate void MyDelegateType (string s);
Заметьте, что делегатов еще нет, есть только схема, которой должны удовлетворять.будущие делегаты. Читайте это так. Все делегаты типа MyDelegateType смогут вызывать методы каких-то классов с такой сигнатурой:
public void Func (string s);
Теперь создадим пару классов и поместим в них функции с указанной сигнатурой. Они могут иметь произвольные имена, но правила игры должны оставаться неизменными. Я специально назвал методы одним именем, чтобы лишний раз показать инкапсуляцию в классе. Два метода с именем Task могут быть заданиями будущего делегата и не вступят в конфликт имен, так как расположены в разных классах.
class A
{
public void Task (string s) { Console.WriteLine(s + " from A.Task"); }
}
class B
{
public void Task (string s) { Console.WriteLine(s + " from B.Task"); }
}
Теперь создадим еще один класс, в котором покажем, как создать реальных делегатов, как передать им список поручений, и как отправить их на работу. Кроме того, для убедительности, вставим в класс Test еще один метод (NewTask), который также будет выполнять роль задания делегата.
class Test
{
public void NewTask (string s) { Console.WriteLine(s + " from NewTask"); }
public static void Main ()
{
Console.WriteLine ("Separate delegates:\n");
A a = new A(); B b = new B(); // Объекты нужны для доступа к методам Task
MyDelegateType d1 = new MyDelegateType (a.Task); // Это — уже реальный делегат с заданием a.Task
d1 ("Hi,"); // Пусть делегат работает
MyDelegateType d2 = new MyDelegateType (b.Task); // Второй реальный делегат с заданием b.Task
d2 ("Hi,"); // Пусть работает
Console.WriteLine ("\n\nOne Combined delegate:\n");
MyDelegateType d = (MyDelegateType)Delegate.Combine (d1, d2); // Более сложный делегат (имеет 2 задания)
d ("Combined: Hi, ");
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.