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

Несмотря на то, что адреса и указатели используются в С# лишь неявно, все-таки удобно интерпретировать этот новый тип следующим образом. Все переменные типа ManDelegate — это объекты класса, умеющего работать с указателями на функции с указанной сигнатурой. Имея такой класс, мы можем создавать объекты, инициализировать их адресом той или иной функции (метода класса) и использовать их для запуска этой функции. В нашем случае этими функциями будут методы Firstname и Surname. Если вам хочется быть более точным, то замените все вхождения слов «указатель» и «адрес» на слово «ссылка». Для тех же, кто привык работать с адресами, истинная сущность происходящего становится более понятной, если использовать понятия указатель и адрес.

Добавьте в класс ManTest объявление коллекции людей:

public ArrayList men;

После этих приготовлений мы можем, создать в классе ManTest метод Show, в котором дадим пользователю возможность выбрать способ вывода информации об объектах, хранимых в списке men. Выбор осуществляется с помощью вспомогательноuj метода ShowMenu.

public char ShowMenu()

{

Console.Write(

  "\n\t\tf - First Names"+

  "\n\t\ts - Surnames"+

  "\n\t\tn - Normal"+

  "\n\t\tq - Quit\n\n");

return Console.ReadLine().ToUpper()[0];

}

Метод Show содержит:

¨  Инициализацию коллекции людей,

¨  Объявление объекта d делегатного класса ManDelegate

¨  Псевдо-бесконечный цикл, в котором мы даем пользователю возможность выбрать способ вывода информации об объектах, хранимых в списке men.

public void Show()

{

Man[] ma = {

           new Man("Ken Wood",40),

           new Man("Lennie Tristano",50),

           new Man("Ben Webster",60)

        };

men = new ArrayList(ma);

ManDelegate d;       //=== Вот он — делегат (объект, который можно вызывать как функцию)

while (true)

{

Console.WriteLine(men.Count == 0 ? "List is empty" : "List of men");

switch (ShowMenu())  // Пользователь выбирает способ вывода информации

{

case 'Q':  // Закачиваем работу

    return;

  case 'F':

    foreach (Man m in men)

    {

                   //=== Инициализируем делегата и вызываем как функцию

      d = new ManDelegate (m.Firstname);

      d();

    }

    break;

  case 'S':

    foreach (Man m in men)

    {

      d = new ManDelegate (m.Surname);

      d();

    }

    break;

  case 'N':        //===== Выводим список обычным образом

    Console.WriteLine ("\n\nLinear List of Man\n");

    foreach (Man m in men)

      Console.WriteLine (m);

    break;

  }

}

}

Для запуска новой версии проекта вам понадобится изменить код функции Main. Сделайте это самостоятельно. Затем запустите и выберите нормальный режим демонстрации списка (3-й пункт меню). Убедиттесь в том, что при выводе данных любого объекта надежно работает все тот же полиморфизм позднего связывания. Именно этот механизм позволяет правильно выбрать на этапе выполнения тело виртуальной функции ToString.

Затем проверьте работу делегатов. При анализе кода обратите внимание на то, как мы создаем новые объекты, являющиеся реальными делегатами (а не просто объявлениями типа). Это происходит в строках:

d = new ManDelegate (m.Firstname);

и

d = new ManDelegate (m.Surname);

При создании делегаты инициализируются ссылкой на функцию Firstname или Surname. Последние являются методами класса Man. Обратите внимание на то, как мы работаем с объектом d. При его создании — это переменная (d =. . .). При запуске делегатной функции — это функция d(). Такая техника вызывает противоречивые чувства. "Чайники" не замечают ничего странного (их вообще трудно удивить).

Как гласит документация, любое объявление делегатного типа означает заключение контракта (похоже на COM, не так ли?), который вводит и закрепляет сигнатуру (в языке С++ мы называли это прототипом) для одного или нескольких методов, которые могут быть использованы делегатами. Если вы объявили переменную делегатного типа, то можете считать, что она ссылается на одну или несколько функций с заданной сигнатурой (прототипом).

Делегат в С# может указывать либо на статическую функцию (static method) класса, либо на обычную функцию (член класса), но он не может указывать просто на функцию, так как в языке отсутствует такое понятие. Мы уже обсуждали тот факт, что в С# нет внешних или глобальных функций.