Пред нами стоит один из часто задаваемых на собеседовании вопросов — Чем различаются Делегаты от Событий? Подробнее об этом в этой статье.
Для начала стоит разобраться с элементами рассматриваемой области, начнем с Делегатов. Говоря самыми приближенными и простыми терминами, можно сказать,что делегат это переменная которая может содержать в себе функцию(метод) которую мы вызываем с помощью нашей переменной-делегата. Одна из проблем понимания делегатов, как явления в языке программирования стоит упомянуть, что такие понятия как Классы-Делегаты и Объекты-Делегаты часто называют просто Делегатами, тем самым смешивая терминологию(делать этого не стоит, но в порыве объяснения чего либо с ними (делегатами) связанного — разрешается).
Объяснения на примере
Итак, Класс-делегат является самым что ни на есть обычным классом Delegate унаследованным от Object и содержит в себе следующие свойства и методы которые чаще всего применимы на практике:
- Target — свойство, которое хранит ссылку на экземпляр объекта в котором содержится метод сообщенный с делегатом, а если метод static то значение равно null.
- Method — свойство, которое хранит имя последнего метода сообщенного с делегатом из класса в котором объявлена переменная-делегат.
- Combine — метод объединяющий списки вызовов двух делегатов.
- GetInvocationList — метод возвращающий массив делегатов , представляющий список вызовов текущего делегата.(массив методов простыми словами).
В свою очередь события выглядят вот так (правда очень похоже).
Ну и живой пример класса в котором используется объект-делегат. В данном классе мы создаем объект-делегат makeNoise и связываем с ним метод MakeFlash. Следует заметить что данный класс так же имеет метод Fire который и будет использован конечным пользователем.
Класс Program в котором реализована основная логика программы. Здесь мы создаем два экземпляра класса Weapon(gun и cannon) и после этого сообщаем с полями-делегатами(makeNoise) методы GunBlast и CannonBlast соответсвенно. А после вызываем метод Fire который в своей реализации "дергает" объект-делегат и вызывает все сообщенные с ним методы.
После работы программы консоль выглядит следующим образом. Произошел вызов совмещенных с полем-делегатом методов из метода Main а так же были вызваны те методы которые были переданы объекту делегату в классе Weapon.
Но что же будет если использовать ключевое слово event?
А ничего программа будет выполняться как и прежде и результат будет тот же, НО есть один нюанс, теперь мы не можем непосредственно у makeNoise вызвать такие методы как Invoke, GetInvocationList, и не получиться обратиться к полям Method и Target. В этом и заключается основное отличие делегатов от событий — вторые боле "приватные", "защищенные", они не имеют никаких методов и полей, мы лишь можем применить к ним операции += и -= которые позволяют связывать и убирать методы из списка вызовов.
Итог
Из всего вышесказанного можно выделить следующее:
- События можно заменить делегатами и ничего не сломается;
- События являются приватно-ориентированной моделью делегатов.
И на последок, говоря простым языком аналогий и объект-делегат, и объект-событие являются некоторыми "ящиками" для методов. Только ящик-делегат прозрачный и мы можем даже его открыть и посмотреть как он устроен, а ящик-событие черного цвета и мы практически не можем с ним взаимодействовать.