Эти две функции в некоторой степени похожи (обе предназначены для динамической диспетчеризации), но их нельзя сравнивать напрямую.
События должны уведомлять другие объекты о том, что объект пришел к какому-то переходу состояния . Это языковая функция, которая воплощает шаблон проектирования Observer . Это может быть полезно во многих случаях, но не всегда полезно или желательно. Это инструмент для выполнения конкретной работы.
Виртуальные функции используются для создания Объектно-ориентированного полиморфизма . Они являются базовым строительным блоком практически для каждого шаблона проектирования и имеют много объектно-ориентированного дизайна.
Чтобы попытаться сравнить их, я предполагаю, что вы пытаетесь реализовать какую-либо форму шаблона наблюдателя с помощью любой функции. С этим ограничением все еще не существует простого правила, к которому можно прибегнуть, чтобы решить, какой из них следует использовать. Вместо этого вам придется задавать себе такие вопросы, как:
- Кто вызвал это: Произойдет ли действие, которое запускает ваш переход состояния внутри, или оно будет вызвано извне?
Если он запускается внутри, вы можете использовать событие или виртуальный метод. Если он запускается извне, вы должны использовать виртуальный метод.
- Кого это волнует: Должен ли класс, определяющий состояние, обрабатывать последствия перехода состояния или внешний класс должен его обрабатывать?
Если класс, которому принадлежит состояние, должен обрабатывать переход, то это должен быть виртуальный метод. Если отдельный класс должен реагировать на переход, это должно быть событие.
- Сколько обработчиков мне нужно: Вам всегда нужен один обработчик, чтобы реагировать на изменение состояния, или вам нужно много?
Если он вам нужен, то можно использовать виртуальный метод или событие. Если вам нужно много, тогда будет намного проще использовать событие.
- Знаю ли я, какой обработчик мне нужен во время компиляции: Связываюсь ли я с одним известным обработчиком или я связываюсь с неизвестными обработчиками, возможно меняющимися со временем?
Если вам нужно изменить свои обработчики, вы должны использовать события. Если у вас есть только один обработчик, который известен во время компиляции, вы можете использовать виртуальный метод.
- Насколько связан должен быть мой код: Принадлежит ли ваш код обработчика производному классу вашего исходного типа или он принадлежит другому месту?
Если он принадлежит к производному классу, вам нужен виртуальный метод. Если оно принадлежит где-то еще, вам нужно событие.
Как видите, ответы будут сильно зависеть от конкретной предметной области и архитектуры объекта. Хороший дизайн - это не то, что волшебным образом попадает к вам на колени через какой-то контрольный список. Надо много думать об этом:)
Edit:
Возможно, это не относится напрямую к событиям C #, но может быть полезно взять пример из существующей работы. Вот краткая статья, которую я только что нашел (отвечая на другой вопрос) об альтернативах проектирования в Java для шаблонов событий: http://csis.pace.edu/~bergin/patterns/event.html