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

Принимая во внимание особенности синтаксиса последней строки тела цикла foreach, вы, вероятно, догадались, что Target является свойством класса Delegate. Аксессор get этого свойства возвращает ссылку на объект, с которым связан делегат. Конечно же эта ссылка совпадает с текущим значением переменной m, поэтому для каждого объекта из списка производится вывод еще одной строки текста, которая генерируется методом ToString класса Man.

Выше мы говорили о неизменяемости делегатов. Это свойство делегатов относится к методам класса Delegate, но не относится к операциям сложения и вычитания, которые, как мы убедились, изменяют список заданий делегата. Чтобы проиллюстрировать неизменяемость делегатов, мы введем, а затем протестируем еще один вариант все того же, исследуемого нами фрагмента программы.

case 'f':

foreach (Man m in men)

{

ManDelegate[] a =  { new ManDelegate(m.Firstname), new ManDelegate(m.Surname) };

d = (ManDelegate)Delegate.Combine(a);

ManDelegate d2 = (ManDelegate)d.Clone();     // Создаем нового делегата на основе старого

Delegate.Remove (d2, a[1]);    // Убираем одну функцию из списка его заданий

d2();            // Просим поработать

}

break;

Сначала мы готовим, а затем и комбинируем список заданий для делегата d. Он состоит из двух ссылок на методы класса Man. Затем мы создаем делегата d2 в виде поверхностной (shallow) копии делегата d. Далее мы пытаемся убрать из списка заданий второго делегата ссылку на метод Surname. Компилятор не возражает. Но, несмотря на это, при запуске делегата (строка d2();) он выполняет оба задания. Это происходит потому, что делегат неизменяем (immutable). Для того, чтобы добиться желаемого результата, необходимо: либо присвоить (то есть, изменить его явным образом) делегату d2 временную версию делегата:

d2 = (ManDelegate)Delegate.Remove (d2, a[1]);

либо создать еще одного делегата, который учтет наши изменения в списке заданий. Сами изменения вносятся, как вы видите, с помощью методов Combine и  Remove класса Delegate, а не с помощью операций + и –.

Приведем еще одну, последнюю версию подопытного фрагмента кода, для того, чтобы познакомить вас с еще одним методом (GetInvocationList) класса Delegate, который возвращает массив делегатов, чей список вызовов, соответствует списку вызовов того делегата, к которому применен метод.

case 1:

foreach (Man m in men)

{

ManDelegate[] a =  { new ManDelegate(m.Firstname), new ManDelegate(m.Surname) };

d = (ManDelegate)Delegate.Combine(a);

//==== Создаем нового делегата на основе массива делегатов, созданного на основе списка вызовов старого делегата

ManDelegate d2 = (ManDelegate)Delegate.Combine (d.GetInvocationList());

d2 += ar[0] + ar[0];          // Корректируем его список вызовов

ManDelegate d3 = (ManDelegate)Delegate.Remove (d2,a[1]); // Коррекция требует создать новый объект

d3();            // Какие методы вызовет этот делегат?

}

break;

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

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

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