C # не может динамически генерировать этот простой обработчик событий - PullRequest
3 голосов
/ 04 марта 2011

Я пытаюсь немного узнать о динамической генерации обработчиков событий, и мне трудно пытаться воссоздать эту простую ситуацию:

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

Я использовал отражатель для генерации IL для DoSomethingметод, и он дает мне:

.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-компилятор обнаружил внутреннее ограничение.

Может кто-нибудь указать, где я ошибся?

[EDIT] Как прокомментировали несколько человек,В целом, генерация IL не нужна, если я знаю все типы.Моя причина для этого заключается в том, что это первый шаг к динамической генерации обработчиков событий во время выполнения для событий, где я не знаю всех задействованных типов.По сути, я следовал примеру в http://msdn.microsoft.com/en-us/library/ms228976.aspx,, но застрял, а затем попытался развернуть вещи в простой пример, чтобы я мог приступить к работе.

Ответы [ 2 ]

5 голосов
/ 04 марта 2011

Неясно, почему вы хотите создать этот метод динамически.Я не могу вспомнить ни одной ситуации, в которой вы не могли бы просто применить лямбду к событию:

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 недопустим, поскольку Ldarg_0 и Ldarg_1 загружают два параметра, но Ldarg_2 относится к несуществующему параметру.Если я изменю его следующим образом, он будет работать, как и следовало ожидать - теперь это статический метод с тремя параметрами, где первый параметр в основном 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");
}

Замените «MyClass» на имя вашего класса.

Что касается EDIT вашего вопроса, вам не нужно генерировать динамический метод путем написания кода 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);
}
0 голосов
/ 16 марта 2011

Как насчет этого сценария ...

У вас есть пары Methods и событий, так что каждая пара - это MethodAsync и MethodCompleted.Каждый MethodCompleted имеет свою сигнатуру (отличающуюся подтипом аргументов событий, который является вторым параметром).

Вы хотите создать оболочку для вызова данного MethodAsync, который перехватывает универсальный обработчик событий вплоть до соответствующегоMethodCompleted.

Обычно вы можете создать метод "void GlobalHandler (object, object)" и выполнить

MethodCompleted += GlobalHandler;  

Однако вы не можете передать объект события, поэтому вам нужно использоватьотражение, чтобы получить ссылку на обработчик события, а затем сделать AddHandler.Опять же, загвоздка в том, что AddHandler не любит полиморфизм (кажется), и жалуется, что MethodCompleted хочет чего-то другого, кроме GlobalHandler.

В таком случае кажется, что вам нужно создать DynamicMethod подписи EXACT, который ожидает MethodCompleted, и затем вызвать GlobalHandler из этого.Я прав, или я тоже что-то здесь упускаю.

...