Опубликован: 25.03.2010 | Доступ: свободный | Студентов: 1447 / 158 | Оценка: 4.31 / 4.00 | Длительность: 25:42:00
Лекция 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. Ввожу код на сайте, пишет:

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

 

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