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

{

  ManDelegate[] a =  {         //===== Создаем массив делегатов

               new ManDelegate(m.Firstname),

               new ManDelegate(m.Surname)

             };

  d = (ManDelegate)Delegate.Combine(a); // Формируем список заданий делегата d

  d.DynamicInvoke (null);            // Запускаем список заданий делегата

  Console.WriteLine (d.Target);    // Выводим объект, ссылку на который хранит делегат

}

break;

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

Выше мы говорили о неизменяемости делегатов. Это свойство делегатов относится к методам класса 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)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, а не с помощью операций + и –. Следующая версия исследуемого фрагмента уже реагирует на удаление ссылок.

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)d.Clone();

      //==== Создаем еще одного делегата, убрав одно задание

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

  d3();         // Просим его поработать

}

break;

Приведем еще одну, последнюю версию подопытного фрагмента кода, для того, чтобы познакомить вас с еще одним методом (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;

Метод GetInvocationList возвращает массив (Delegate[]) ссылок на все задания одного делегата, если он имеет тип multicast (combinable). Наш — такой. Здесь хочется отметить ту легкость, даже какую-то обыденность, с которой многие методы большинства классов библиотеки .Net Framework оперируют динамическими структурами данных. Понадобился массив делегатов? Это просто, сейчас будет создан и возвращен в виде ссылки (читай, адреса его начала). Полагаю, что вы обрели достаточный навык и даже некоторый опыт в управлении делегатами на основе обычного метода класса.