在我的单元测试中,我正在尝试动态包装一个事件,这样我就可以在转发到实际的处理程序之前注入一些测试代码。这就是我现在所拥有的:
Delegate handler = // the original method handler
object myObject = // the original object
EventInfo ei = myObject.GetType().GetEvent("MyEvent");
Delegate d = GenerateWrappedDelegate(ei);
ei.AddEventHandler(myObject, d);
...
private GenerateWrappedDelegate(EventInfo eventInfo)
{
var eventHandlerType = eventInfo.EventHandlerType;
int arity = eventHandlerType.GetMethod("Invoke").GetParameters().Count();
var methodName = string.Format("Arity{0}", arity);
var eventRegisterMethod = typeof(EventMonitor).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
return Delegate.CreateDelegate(eventHandlerType, this, eventRegisterMethod);
}
private void Arity1(object arg1)
{
Handle(() => handler.DynamicInvoke(arg1));
}
private void Arity2(object arg1, object arg2)
{
Handle(() => handler.DynamicInvoke(arg1, arg2));
}
private void Handle(Action action)
{
// Do something interesting here, before the original event handler is called
action();
}
我有很多这些Arity
方法,因为我事先并不知道我的事件将有多少参数,我想将它们全部传递给原始委托。
如果我的事件处理程序委托看起来像这样,那么这一切都可以正常工作:
public delegate void MyDelegate(object sender, EventArgs e);
public event MyDelegate MyEvent;
但是,我们代码库中的一些代表看起来不像“传统”事件处理程序,但更像是这样:
public delegate void MyOtherDelegate(object sender, int updatedValue);
public event MyOtherDelegate MyOtherEvent;
然后,我的代码突然不再起作用了;它在int
上失败了。
我通过在if
中添加了一个复杂的GenerateWrappedDelegate
语句并添加了Arity
方法解决了这个问题,如下所示:
private void Arity2oo(object arg1, object arg2)
{
Handle(() => handler.DynamicInvoke(arg1, arg2));
}
private void Arity2oi(object arg1, int arg2)
{
Handle(() => handler.DynamicInvoke(arg1, arg2));
}
但是这显然不能很好地扩展,而且这很麻烦。
我一直在研究如here所述生成动态方法,但这让我更加困惑。我不熟悉MSIL,我不知道如何基于它们的运行时类型生成加载所有参数的方法体,并且每个参数都调用DynamicInvoke
。我想它看起来不会很漂亮。
我们正在使用.NET 4.0,所以我也一直在研究dynamic
,但我也无法找到一种方法来实现这一目标。
我没有想法。你有吗?
答案 0 :(得分:1)
您的代码在int
上失败,因为.NET中的协方差/逆转不适用于值类型(必须装箱)。您可以使用泛型来解决这个直接问题:
private GenerateWrappedDelegate(EventInfo eventInfo)
{
var eventHandlerType = eventInfo.EventHandlerType;
var parameters = eventHandlerType.GetMethod("Invoke").GetParameters();
var methodName = string.Format("Arity{0}", parameters.Length);
var eventRegisterMethod = typeof(EventMonitor)
.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance)
.MakeGenericMethod(parameters.Select(p => p.ParameterType).ToArray());
return Delegate.CreateDelegate(eventHandlerType, this, eventRegisterMethod);
}
private void Arity1<T>(T arg1)
{
Handle(() => handler.DynamicInvoke(arg1));
}
private void Arity2<T1, T2>(T1 arg1, T2 arg2)
{
Handle(() => handler.DynamicInvoke(arg1, arg2));
}
虽然这并未涵盖所有可能的代理(例如,ref / out参数需要单独的重载),但我怀疑您遇到具有此类签名的事件的可能性极小。