Опубликован: 25.03.2010 | Уровень: для всех | Доступ: платный
Лекция 10:

Интерфейсы, делегаты, события в C#

Методы DynamicInvoke() и GetInvocationList()

Этот метод позволяет вызывать отдельные члены, адресуемые списком объекта-делегата, и задавать требуемые аргументы. Если член списка не имеет аргументов, то в качестве параметра метода используется null, иначе - массив параметров адресуемого члена.

Мы убедились, что объявление ссылки на объект-делегат прописывает только прототип методов, которые корректно может адресовать эта ссылка. Сам делегат как объект создается при заполнении списка адресуемыми функциями. Но при формировании списка в него добавляются только имена методов. Это приемлемо, если вызываются функции с пустой сигнатурой.

using System;
    
namespace Test
{
    class ShowPerson
    {
        // Объявляем функцию с аргументами
        public static void Handler(string name, int age)
        {
            Console.WriteLine("Сотрудник {0}, возраст {1}", name, age);
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        public static string Title = "Вызов методов с параметрами";
    
        // Объявляем делегат как член класса
        delegate void MyDelegate(string name, int age);
    
        public MyClass()
        {   
            // Создаем и заполняем объект-делегат
            MyDelegate del = new MyDelegate(ShowPerson.Handler);
            // Добавляем другие ссылки
            int count = 3;
            for (int i = 1; i < count; i++)
            {
                del += ShowPerson.Handler;
            }
    
            // Вызываем цепочку методов с одинаковым параметром
            del("Иванов", 21);
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = MyClass.Title;
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 30;
            Console.WindowHeight = 4;
    
            new MyClass();// Исполняем
    
            Console.ReadLine();
        }
    }
}
Листинг 10.13 . Делегирование вызова методов с параметрами

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

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

public object DynamicInvoke(params object[ ] args)

Но есть еще одна важная проблема, которую можно решить с помощью метода DynamicInvoke(), это надежность кода при адресации вызовов методов посредством делегата. Дело в том, что любой из цепочки метод при традиционном вызове может дать сбой. В результате система выдаст исключение и прервет вызовы остальных методов списка. Исключение мы можем обработать, но только для списка в целом, если не будем контролировать индивидуально каждый член списка делегата. Например, в приведенном фрагменте кода что-то успеет выполниться до выброса исключения, а что-то нет:

try
{
del(null, 0);
}
catch
{            }

Эти проблемы решает метод DynamicInvoke() совместно с GetInvocationList(), как показано в следующем примере

using System;
    
namespace Test
{
    class ShowPerson
    {
        // Функция с аргументами
        public static void Handler(string name, int age)
        {
            Console.WriteLine("Сотрудник {0}, возраст {1}", name, age);
        }
    
        // Проблемная функция с нормальным прототипом
        public static void ProblemHandler(string name, int age)
        {
            // Преднамеренно выбрасываем исключение
            throw new Exception();
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        public static string Title = "Применение DynamicInvoke()";
    
        // Объявляем делегат
        delegate void MyDelegate(string name, int age);
    
        public MyClass()
        {
            //
            // Формируем список объекта-делегата
            //
            // Добавляем в список один проблемный метод
            MyDelegate del = new MyDelegate(ShowPerson.ProblemHandler);
    
            // Добавляем еще три нормальных метода
            int count = 3;
            for (int i = 0; i < count; i++)
            {
                del += ShowPerson.Handler;
            }
    
            object[] param = new object[2];// Объявили массив для параметров
            int j = 0;    // Объявили и инициализировали счетчик
            // Перебираем список вызовов делегата, включая и вызов проблемного метода
            foreach (Delegate d in del.GetInvocationList())
            {
                // Индивидуально формируем параметры методов
                switch (j)
                {
                    case 0:// Можно и не задавать, все равно для проблемного метода!
                        param[0] = "Мистер X";
                        param[1] = 99;
                        break;
                    // Для вызовов нормального метода
                    case 1:
                        param[0] = "Иванов";
                        param[1] = 21;
                        break;
                    case 2:
                        param[0] = "Петров";
                        param[1] = 22;
                        break;
                    case 3:
                        param[0] = "Сидоров";
                        param[1] = 23;
                        break;
                }
    
                j++;    // Счетчик
    
                // Защищенно вызываем адресуемые методы индивидуально 
                try
                {
                    d.DynamicInvoke(param);
                }
                catch (Exception exc)
                {
                    string str = d.Method.Name;
                    Console.WriteLine("Сбой метода {0}!!!", str);
    
                    str = exc.Message;   // Системное сообщение
                    // Разбиваем длинное сообщение пополам
                    int pos = str.Length / 2;
                    pos = str.IndexOf(' ', pos);// От средины первый пробел
                    Console.WriteLine("\"" +    // Экранируем кавычки
                        str.Substring(0, pos) +
                        Environment.NewLine +
                        str.Substring(pos + 1) +
                        "\"");                  // Экранируем кавычки
                    Console.WriteLine();        // Отделяем сообщение
                }
            }
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = MyClass.Title;
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 32;
            Console.WindowHeight = 8;
    
            new MyClass();// Исполняем
    
            Console.ReadLine();
        }
    }
}
Листинг 10.14 . Применение метода DynamicInvoke()

Перегруженные операторы 'operator ==' и 'operator !='

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

using System;
    
namespace Test
{
    class Handler
    {
        // Функции
        public void Handler1()
        {
        }
    
        public void Handler2()
        {
            ;
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        public static string Title = "Применение 
          операторов '==' и '!='";
    
        // Объявляем делегат
        delegate void MyDelegate();
    
        // Объявляем ссылки на делегаты как 
        // поля для видимости в методах класса
        MyDelegate del0, del1;
    
        public MyClass()
        {
            // Создаем объект
            Handler obj = new Handler();
    
            // Формируем список вызовов объекта-делегата
            del0 = new MyDelegate(obj.Handler1);
            del0 += new MyDelegate(obj.Handler2);
    
            // Еще один делегат с тем же списком вызовов
            del1 = new MyDelegate(obj.Handler1);
            del1 += obj.Handler2;   // Упрощенный синтаксис
    
            // Сравниваем делегаты с полность совпадающими списками
            Compare();
    
            // Делегат прежним содержимым, но в другом порядке
            del1 = new MyDelegate(obj.Handler2);
            del1 += obj.Handler1;   // Упрощенный синтаксис
    
            // Сравниваем делегаты с одинаковым содержимым, но разным порядком
            Compare();
    
            // Изменяем содержимое одного из делегатов
            del0 -= obj.Handler2;
    
            // Опять сравниваем делегаты с разным содержимым
            Compare();
        }
    
        void Compare()
        {
            if (del0 == del1)
                Console.WriteLine("Списки делегатов идентичны");
            else
                Console.WriteLine("Списки делегатов различны");
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = MyClass.Title;
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 33;
            Console.WindowHeight = 4;
    
            new MyClass();// Исполняем
    
            Console.ReadLine();
        }
    }
}
Листинг 10.15 . Перегруженные операторы 'operator ==' и 'operator !='

Обратите внимание, что для равенства делегатов важен не только состав функций, но и порядок их следования в списке.

Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.

 

Как активировать код?

Денис Пашков
Денис Пашков
Россия
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974