3

イベントハンドラーの動的生成について少し学習しようとしていますが、この単純な状況を再現するのに苦労しています:

public delegate void SomethingHappenedEventHandler(object sender, object args);
public event SomethingHappenedEventHandler SomethingHappened;

// This is the event handler that I want to create dynamically
public void DoSomething(object a, object b)
{
    DoSomethingElse(a, b);
}

public void DoSomethingElse(object a, object b)
{
    Console.WriteLine("Yay! " + a + " " + b);
}

リフレクターを使用して DoSomething メソッドの IL を生成すると、次のようになります。

.method public hidebysig instance void DoSomething(object a, object b) cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.1 
    L_0002: ldarg.2 
    L_0003: call instance void MyNamespace::DoSomethingElse(object, object)
    L_0008: ret 
}

そこで、DoSomething(...) と同等のメソッドを動的に生成して実行する次のコードを作成しました。

public void CreateDynamicHandler()
{
    var eventInfo = GetType().GetEvent("SomethingHappened");
    var eventHandlerType = eventInfo.EventHandlerType;

    var dynamicMethod = new DynamicMethod("DynamicMethod", null, new[] { typeof(object), typeof(object) }, GetType());
    var ilgen = dynamicMethod.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Ldarg_2);

    MethodInfo doSomethingElse = GetType().GetMethod("DoSomethingElse", new[] { typeof(object), typeof(object) });
    ilgen.Emit(OpCodes.Call, doSomethingElse);
    ilgen.Emit(OpCodes.Ret);

    Delegate emitted = dynamicMethod.CreateDelegate(eventHandlerType);
    emitted.DynamicInvoke("hello", "world");
}

ただし、これを実行すると、InvalidProgramException が発生します。JIT コンパイラで内部制限が発生しました。

誰かが私が間違っている場所を指摘できますか?

[編集] 何人かがコメントしているように、関連するすべてのタイプを知っていれば、IL 生成全体は不要です。これを行う理由は、これが、関連するすべてのタイプがわからないイベントに対して、実行時にイベント ハンドラーを動的に生成するための最初のステップだからです。基本的に、私はhttp://msdn.microsoft.com/en-us/library/ms228976.aspxの例に従っていましたが、動けなくなってから、作業を開始できる単純な例に巻き戻そうとしました。

4

2 に答える 2

5

このメソッドを動的に作成する理由は不明です。イベントにラムダを適用できない状況は考えられません。

public delegate void SomethingHappenedEventHandler(object sender, object args);
public event SomethingHappenedEventHandler SomethingHappened;

public void DoSomethingElse(object a, object b)
{
    Console.WriteLine("Yay! " + a + " " + b);
}

// If the signature exactly matches the delegate, just use the method name
SomethingHappened += DoSomethingElse;

public void DoSomethingDifferent(object a)
{
    Console.WriteLine("Yay! " + a);
}

// Otherwise, just use a lambda expression
SomethingHappened += (a, b) => DoSomethingDifferent(a);

とはいえ、コードが機能しない理由は、DynamicMethod静的メソッドのみを生成するためです。したがって、ILコードは無効です。2つのパラメータLdarg_0Ldarg_1ロードしますが、Ldarg_2存在しないパラメータを参照しているためです。次のように変更すると、期待どおりに機能します。これは、3つのパラメーターを持つ静的メソッドになり、最初のパラメーターは基本的に次のようになりthisます。

public void CreateDynamicHandler()
{
    var dynamicMethod = new DynamicMethod("DynamicMethod", null,
        new[] { typeof(MyClass), typeof(object), typeof(object) }, typeof(MyClass));
    var ilgen = dynamicMethod.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldarg_1);
    ilgen.Emit(OpCodes.Ldarg_2);

    MethodInfo doSomethingElse = typeof(MyClass).GetMethod("DoSomethingElse",
        new[] { typeof(object), typeof(object) });
    ilgen.Emit(OpCodes.Call, doSomethingElse);
    ilgen.Emit(OpCodes.Ret);

    Delegate emitted = dynamicMethod.CreateDelegate(
        typeof(Action<MyClass, string, string>));
    emitted.DynamicInvoke(this, "Hello", "World");
}

「<code>MyClass」をクラスの名前に置き換えます。

質問の編集に関しては、実行時にメソッドを動的に呼び出すために、ILコードを記述して動的メソッドを生成する必要はありません。たとえば、Reflectionを使用するだけです。

public void DoSomething(object a, object b)
{
    var method = GetType().GetMethod("DoSomethingElse", BindingFlags.Instance | BindingFlags.Public);
    method.Invoke(this, new object[] { a, b });
}

また:

// Note “static”
public static void DoSomething(dynamic instance, object a, object b)
{
    // This will call whatever “DoSomethingElse” method exists on the type
    // that “instance” has *at run-time*
    instance.DoSomethingElse(a, b);
}
于 2011-03-04T13:16:43.343 に答える
0

このシナリオはどうですか...

メソッドとイベントのペアがあり、各ペアはMethodAsyncとMethodCompletedです。各MethodCompletedには、異なるシグニチャがあります(2番目のパラメータであるeventargsのサブタイプが異なります)。

汎用イベントハンドラーを対応するMethodCompletedにフックする特定のMethodAsyncを呼び出すためのラッパーを作成するとします。

通常、メソッド「void GlobalHandler(object、object)」を作成して、

MethodCompleted += GlobalHandler;  

ただし、イベントオブジェクトを渡すことはできないため、リフレクションを使用してイベントハンドラーへの参照を取得してから、AddHandlerを実行する必要があります。繰り返しになりますが、問題は、AddHandlerがポリモーフィズムを好まないことであり(そう思われます)、MethodCompletedがGlobalHandler以外のものを必要としていると不平を言います。

このような場合、MethodCompletedが期待するEXACTシグニチャのDynamicMethodを作成し、そこからGlobalHandlerを呼び出す必要があるようです。私は正しいのか、それともここで何かが足りないのか。

于 2011-03-16T12:12:13.987 に答える