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_
,它确实not有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);
此外,无法创建一个继承 interop 接口的接口来解决该问题,因为它还将继承编组的add
/remove
方法。
此问题已在此处的 Moq 问题跟踪器上报告:http://code.google.com/p/moq/issues/detail?id=226 http://code.google.com/p/moq/issues/detail?id=226
Update:
在 Moq 开发人员解决这个问题之前,唯一的解决方法可能是使用反射来修改接口,这似乎违背了使用 Moq 的目的。不幸的是,对于这种情况,“自行设计”起订量可能会更好。
此问题已在 Moq 4.0(2011 年 8 月发布)中得到修复。