Moq 'перехватывает' события, обнаруживая вызовы внутренних методов события. Эти методы называются add_
+ именем события и являются «специальными» в том смысле, что они являются нестандартными методами C #. События в некоторой степени похожи на свойства (get
/ set
) и могут быть определены следующим образом:
event EventHandler MyEvent
{
add { /* add event code */ };
remove { /* remove event code */ };
}
Если вышеупомянутое событие было определено на интерфейсе, который будет Moq'd, следующий код будет использован для вызова этого события:
var mock = new Mock<IInterfaceWithEvent>;
mock.Raise(e => e.MyEvent += null);
Поскольку в C # невозможно напрямую ссылаться на события, Moq перехватывает все вызовы методов в Mock и проверяет, был ли вызов добавить обработчик события (в приведенном выше случае добавляется нулевой обработчик). Если это так, ссылка может быть косвенно получена как «цель» метода.
Метод обработчика событий обнаруживается Moq, используя отражение в качестве метода, начинающегося с имени add_
и с установленным флагом IsSpecialName
. Эта дополнительная проверка предназначена для фильтрации вызовов методов, не связанных с событиями, но с именем, начинающимся с add_
.
В этом примере перехваченный метод будет называться add_MyEvent
и будет иметь установленный флаг IsSpecialName
.
Однако, похоже, что это не совсем верно для интерфейсов, определенных во взаимодействиях, поскольку, хотя имя метода обработчика событий начинается с add_
, для не установлен флаг IsSpecialName
. Это может быть связано с тем, что события маршалируются с помощью кода нижнего уровня в функции (COM), а не являются истинными «особыми» событиями C #.
Это можно показать (следуя вашему примеру) с помощью следующего теста NUnit:
MethodInfo interopMethod = typeof(ApplicationEvents4_Event).GetMethod("add_WindowActivate");
MethodInfo localMethod = typeof(LocalInterface_Event).GetMethod("add_WindowActivate");
Assert.IsTrue(interopMethod.IsSpecialName);
Assert.IsTrue(localMethod.IsSpecialName);
Кроме того, нельзя создать интерфейс, который наследует интерфейс от взаимодействия для решения проблемы, так как он также унаследует маршаллированные методы add
/ remove
.
Эта проблема была зарегистрирована на трекере проблем Moq здесь:
http://code.google.com/p/moq/issues/detail?id=226
Обновление:
Пока это не решено разработчиками Moq, единственным обходным решением может быть использование отражения для изменения интерфейса, который, кажется, не соответствует цели использования Moq. К сожалению, для этого случая может быть лучше просто «бросить свой собственный» Moq.
Эта проблема была исправлена в Moq 4.0 (выпущено в августе 2011 г.).