在动态方法中包装事件

时间:2013-10-13 13:56:13

标签: c# reflection delegates

在我的单元测试中,我正在尝试动态包装一个事件,这样我就可以在转发到实际的处理程序之前注入一些测试代码。这就是我现在所拥有的:

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,但我也无法找到一种方法来实现这一目标。

我没有想法。你有吗?

1 个答案:

答案 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参数需要单独的重载),但我怀疑您遇到具有此类签名的事件的可能性极小。