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

しかし、それは明らかにうまくスケーリングできず、非常に冗長です。

hereで説明されているように、動的メソッドの生成を検討してきましたが、これによりさらに混乱します。私は 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 に答える