Новые средства языка. Индексаторы. Делегат на основе обычного метода класса. Делегаты на основе класса MulticastDelegate, страница 3

}

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

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, ");