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

Статический или обычный?

В настоящий момент делегат связывается с обычными методами класса Man. Альтернативой является связывание со статическими методами. С тем, чтобы вы твердо усвоили делегатный синтаксис, мы рассмотрим и второй способ.

Чем отличаются два возможных варианта инициализации делегатов? Если в качестве делегата указан обычный, не статический метод класса, то он хранит не только ссылку на точку входа в функцию, но также и ссылку на конкретный объект класса. Последний определяется как target (цель). Запомните, что в этом случае ссылка (target) не может быть нулевой (null). Однако, она обязательно будет нулевой в случае, если в качестве делегата был заявлен статитеский метод класса.

Сейчас мы применяем довольно затратный способ работы с делегатами, так как для каждого «человека» из списка создаем своего собственного делегата. Такой подход к обработке элементов длинного списка задаст немало работы сборщику мусора, так как ненужные (уже отработавшие) делегаты занимают память в области heap, которую он призван освобождать. При использовании же статических методов мы для обработки всех «людей» из списка смогли бы обойтись лишь одним делегатом.

Так как обычный метод вызывается для какого-то конкретного объекта класса, то он знает с кем работает и ему нет необходимости передавать ссылку на объект класса. Коды методов Surname и Firstname при этом подходе проще, чем необходимо при вызове статических методов. Статический метод не привязан ни к какому объекту и для того, чтобы он все-таки воздействовал на какой-то конкретный объект, ему надо передать ссылку на него. В связи с этим, нам придется изменить сигнатуру делегата. Замените объявление делегатного типа.

//=== Теперь делегатные функции требуют параметр

public delegate void ManDelegate(Man m);

Теперь подправьте методы Firstname и Surname так, чтобы они работали в статическом варианте.

public static void Firstname(Man m)

{

int n = m.name.IndexOf(' ');

if (n < 0)

  n = m.name.Length;

Console.WriteLine("  {0}",m.name.Substring(0, n));

}

public static void Surname(Man m)

{

Console.WriteLine("  {0}", m.name.Substring (m.name.LastIndexOf (' ') + 1));

}

Вспомните, что указатель или ссылку, как и любую другую переменную, можно передавать в качестве параметра функции. То же можно делать и с делегатом. Введите в состав класса ManTest метод HandleMen, который будет проходить по всему списку объектов men (типа ArrayList) и выполнять для каждого из них те действия, которые приписаны делегату (читай, статические методы класса Man).

public void HandleMen (string title, ManDelegate func)

{

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

foreach (Man m in men)

  func(m);       // Запуск делегатной функции с параметром

}

Обратите внимание на второй парамер функции HandleMen. Он имеет тип делегата (приближенно — указателя на функцию). Если мы вызовем HandleMen и передадим ей в качестве второго параметра делегата, начиненного адресом Firstname, то мы увидим список имен, а если передать делегата, настроенного на функцию Surname, то будут выведены фамилии. Теперь замените обе ветви переключателя в методе Show (мы приведем лишь одну).

case 1:

HandleMen("List of Man Names:", new ManDelegate(Man.Firstname));

break;

Запустите и все должно работать так же как и ранее. Более того, почувствовав уверенностьв делегатной арифметике, мы можем управлять делегатами самым произвольным образом. Например, так.

case 1:

ManDelegate

  f = new ManDelegate(Man.Firstname),

  s = new ManDelegate(Man.Surname),

  d = f + s;

  d += f + s;

HandleMen("List of men:", d);

break;

Интересно, что с помощью операции вычитания можно удалять задания из списка, существующего в данный момент. Например, вы можете опробовать такую версию:

case 1:

ManDelegate

  f = new ManDelegate(Man.Firstname),

  s = new ManDelegate(Man.Surname),

  d = f + s;

  d += f + s;

HandleMen("List of men:", d);

d -= f + s + f;    // Корректируем список заданий делегата

HandleMen("List of men:", d);       // Вновь просим выполнить все задания

break;

Она выведет пять строк текста для каждого объекта из списка «людей». Четыре — как и в предыдущем случае, а пятую — при запуске скорректированного делегата.