Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

MulticastDelegate.GetInvocationList




Delegate.Invoke или что там внутри?

Напомним, как выглядит механизм вызова делегата через обращение к методу Invoke.

ldstr "Hello, world!"callvirt instance void App/MyDelegate::Invoke(string)

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

del("Hello, World!")

Дело в том, что метод Invoke недокументирован. Он не является членом классов Delegate и MulticastDelegate. Соответственно, можно предположить, что это специальный метод, генерируемый компилятором. Для проверки предположения обратимся к IL-коду. Действительно, там обнаружится метод Invoke, его код приведен ниже.

.method public hidebysig virtual instance void Invoke(string s) runtime managed{}

Его изучение показывает, что метод лишен какого-либо кода. Он попросту пустой и ничего не может делать. Так может показаться, если не принимать во внимание спецификатор runtime, используемый при определении метода. Данный спецификатор указывает на то, что метод будет реализован по ходу исполнения программы самой средой исполнения. Это означает, что код, обрабатывающий вызов метода, располагается в недрах самой среды исполнения. А она прекрасно осведомлена о внутреннем устройстве делегата и справится с вызовом всех его методов без дополнительной помощи со стороны программиста.

Помимо этого можно отметить, что конструктор каждого делегата описывается подобным образом. Для примера приведем код конструктора одного из делегатов.

.method public hidebysig specialname rtspecialname instance void.ctor(object 'object', native int 'method') runtime managed{}

Код, реализующий делегат, также находится внутри среды исполнения. Оказывается, метод Invoke отвечает не только за вызов делегата, но также за хранение информации о прототипе методов, на которые может ссылаться делегат. Не правда ли, изящное решение? Прототип закодирован в самом методе Invoke, то есть его прототип полностью совпадает с прототипом делегата. Таким образом, не надо прибегать к использованию дополнительных средств, вроде MethodInfo, для хранения информации о прототипе. К тому же, это страхует от возможности случайной передачи неверного количества или типа параметров при классическом обращении к делегату.

Возвращает список делегатов, находящихся в списке вызовов делегата.

public sealed override Delegate[] GetInvocationList();

Несмотря на кажущуюся абстрактность метода, он имеет очень важное практическое применение.

Существует два недостатка механизма вызова функций, связанных с делегатом. И оба они связаны со случаем, когда в списке вызова делегата присутствует более одной функции.

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

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

Особое волнение вызывает вторая проблема, поскольку важные сообщения попросту могут не дойти до адресата. Что неприемлемо при разработке надежных систем. Рассмотрим пример (листинг 9).

Листинг 9. Демонстрация ненадежности делегатов, ссылающихся на несколько функций

using System; class App{ delegate void MyDelegate(string s); // Первый метод, на который мы будем ссылаться при помощи // делегата. Именно он будет вызывать исключение, // не позволяющее обратиться ко второму методу. static void MyHandler(string s) { Console.WriteLine("Handler invoked"); // А здесь произойдет исключение. throw new Exception(); } // Второй метод, на который мы будем ссылаться из делегата. static void MyHandler2(string s) { // Если вдруг эта строка будет выведена, значит // предположения о ненадежности делегатов неверны. Console.WriteLine("Handler2 invoked"); } // Точка входа в программу. static void Main() { // Создадим экземпляр описанного выше делегата. MyDelegate del = new MyDelegate(MyHandler); // Добавим к делегату еще одну ссылку на нашу функцию. del += new MyDelegate(MyHandler2); // Введем дополнительный защищенный блок, // чтобы отлавливать происходящие исключения. try { // Вызовем функции, связанные с делегатом. del("Hello, world!"); } // Введем обработчик исключения, который предотвратит "падение" // программы. catch (Exception ex) { // Сообщим пользователю о том, что произошло исключение. Console.WriteLine("Problem! Exception caught"); } }};

В результате работы программы на консоль будут выведены следующие строки.

Handler invokedProblem! Exception caught

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

Обойти проблему можно следующим образом. Для этого придется воспользоваться методом GetInvocationList, чтобы организовать обращение к делегатам полностью в ручном режиме. Преимущество подхода заключается в том, что можно полноценно контролировать обращение к каждому методу, на который ссылается делегат. Код примера вы обнаружите в листинге 10.

Листинг 10. Надежный способ работы с делегатами, ссылающимися на несколько методов.

using System; class App{ delegate void MyDelegate(string param); // Первый метод, на который мы будем ссылаться при помощи // делегата. Именно он будет вызывать исключение, // не позволяющее обратиться ко второму методу. static void Handler1(string param) { // Выведем на консоль значение переданного параметра, // а также уведомим пользователя о том, что данный // метод был вызван. Console.WriteLine("Handler1 invoked, param = {0}",param); // Преднамеренно выбросим исключение. throw new Exception(); } // Второй метод, на который мы будем ссылаться из делегата. static void Handler2(string param) { // Сообщим пользователю о том, что метод // был вызван, а также выведем на консоль // значение переданного параметра. Console.WriteLine("Handler2 invoked, param = {0}",param); } // Точка входа в программу. static void Main() { // Создадим экземпляр описанного делегата. MyDelegate del = new MyDelegate(Handler1); // Присоединим к нему еще одну функцию. del += new MyDelegate(Handler2); // Последовательно пройдем по каждому делегату, входящему // в список вызова ранее созданного делегата. foreach (MyDelegate d in del.GetInvocationList()) { // Обернем вызов функции в защищенный // блок, что позволит предотвратить // преждевременное завершение алгоритма. try { d("Hello"); } // Это блок обработки исключений, произошедших // в защищенном блоке. catch(Exception ex) { // Сообщим пользователю о том, что при попытке // вызова одной из функций произошло исключение. Console.WriteLine("Oh mama, some exception has occurred"); } } }};

В результате работы программы на консоль будут выведены следующие строки.

Handler1 invoked, param = HelloOh mama, some exception has occurredHandler2 invoked, param = Hello

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

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

Отметим, что обращаться к методам можно не только через функцию DynamicInvoke. Для этих целей также можно использовать свойство Method. А точнее, метод Invoke, предоставляемый классом MethodInfo, экземпляр которого можно получить через вышеупомянутое свойство класса Delegate. В итоге, код вызова можно заменить следующим:

// Было.d.DynamicInvoke(param)// Стало.d.Method.Invoke(d.Target,param)

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




Поделиться с друзьями:


Дата добавления: 2014-01-07; Просмотров: 402; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.007 сек.