1

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

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));
}

但这显然不能很好地扩展,而且非常多余。

我一直在研究生成此处描述的动态方法,但这让我更加困惑。我不熟悉 MSIL,而且我不知道如何生成一个方法体,该方法体根据它们的运行时类型加载所有参数,并调用DynamicInvoke它们中的每一个。我想它看起来不会很漂亮。

我们正在使用 .NET 4.0,所以我也一直在研究dynamic,但我也无法找到一种方法来实现它。

我没主意了。你有什么?

4

1 回答 1

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

于 2013-10-13T16:11:22.183 回答