DynamicMethod.CreateDelegate 的异常,几乎相同的 MSDN 示例

发布于 2024-11-15 14:48:30 字数 2536 浏览 4 评论 0原文

当我调用 CreateDelegate(delegateType) 时,我得到一个 System.ArgumentException ,根据 MSDN,这是因为 delegateType 的参数数量错误或参数类型错误。

奇怪的是我使用的代码几乎都是从 MSDN 复制的。我的整体功能:

public static void AssertRaisesEvent(Action action, object obj, string eventName, NumberOfTimes numberOfTimesRaised)
{
    eventCounter = 0;
    EventInfo eventInfo = obj.GetType().GetEvent(eventName);
    Type tDelegate = eventInfo.EventHandlerType;

    Type returnType = GetDelegateReturnType(tDelegate);
    if (returnType != typeof(void))
        throw new ApplicationException("Delegate has a return type.");

    var handler =
        new DynamicMethod("CompletedHandler",
            typeof(int),
            GetDelegateParameterTypes(tDelegate),
            obj.GetType());

    // Generate a method body. This method loads a string, calls 
    // the Show method overload that takes a string, pops the 
    // return value off the stack (because the handler has no
    // return type), and returns.
    //
    ILGenerator ilgen = handler.GetILGenerator();
    FieldInfo counterFieldInfo = typeof (AssertionHelpers).GetField("eventCounter",
                                                                    BindingFlags.NonPublic | BindingFlags.Static);
    ilgen.Emit(OpCodes.Ldfld, counterFieldInfo);
    ilgen.Emit(OpCodes.Ldc_I4, 1);
    ilgen.Emit(OpCodes.Add);
    ilgen.Emit(OpCodes.Pop);
    ilgen.Emit(OpCodes.Ret);

    // Complete the dynamic method by calling its CreateDelegate
    // method. Use the "add" accessor to add the delegate to
    // the invocation list for the event.
    //

    var delParams = GetDelegateParameterTypes(tDelegate);
    var handlerParams = handler.GetParameters();

    Delegate dEmitted = handler.CreateDelegate(tDelegate);
    eventInfo.GetAddMethod().Invoke(obj, new Object[] { dEmitted });

    ...

正如你所看到的,评论甚至在那里。正如您还可以看到的,我有 delParams 和 handlerParams 变量,它们具有相同数量、相同类型的参数。

这是怎么回事?

MSDN:http://msdn.microsoft.com/en-us/library/ms228976 .aspx

编辑: 我试图绑定的事件:

private NullTransaction transaction;

public delegate void CompletedEventHandler(object testParam);

internal class NullTransaction : ITransaction
{
public event CompletedEventHandler Completed;
    public void Dispose()
    {
        // no implementation
    }

    public void Complete()
    {
        // no implementation
    if(Completed != null)
            Completed.Invoke(this);
    }
}

When i call CreateDelegate(delegateType) i get a System.ArgumentException, which according to MSDN is because the delegateType has the wrong number of parameters or the wrong parameter types.

The strange part is the code I'm using is almost all copied from MSDN. My function as whole:

public static void AssertRaisesEvent(Action action, object obj, string eventName, NumberOfTimes numberOfTimesRaised)
{
    eventCounter = 0;
    EventInfo eventInfo = obj.GetType().GetEvent(eventName);
    Type tDelegate = eventInfo.EventHandlerType;

    Type returnType = GetDelegateReturnType(tDelegate);
    if (returnType != typeof(void))
        throw new ApplicationException("Delegate has a return type.");

    var handler =
        new DynamicMethod("CompletedHandler",
            typeof(int),
            GetDelegateParameterTypes(tDelegate),
            obj.GetType());

    // Generate a method body. This method loads a string, calls 
    // the Show method overload that takes a string, pops the 
    // return value off the stack (because the handler has no
    // return type), and returns.
    //
    ILGenerator ilgen = handler.GetILGenerator();
    FieldInfo counterFieldInfo = typeof (AssertionHelpers).GetField("eventCounter",
                                                                    BindingFlags.NonPublic | BindingFlags.Static);
    ilgen.Emit(OpCodes.Ldfld, counterFieldInfo);
    ilgen.Emit(OpCodes.Ldc_I4, 1);
    ilgen.Emit(OpCodes.Add);
    ilgen.Emit(OpCodes.Pop);
    ilgen.Emit(OpCodes.Ret);

    // Complete the dynamic method by calling its CreateDelegate
    // method. Use the "add" accessor to add the delegate to
    // the invocation list for the event.
    //

    var delParams = GetDelegateParameterTypes(tDelegate);
    var handlerParams = handler.GetParameters();

    Delegate dEmitted = handler.CreateDelegate(tDelegate);
    eventInfo.GetAddMethod().Invoke(obj, new Object[] { dEmitted });

    ...

As you can see the comments are even there. As you also can see i have delParams and handlerParams variables which have the same number of parameters of the same type.

What is going on here?

MSDN: http://msdn.microsoft.com/en-us/library/ms228976.aspx

EDIT:
The event im trying to bind to:

private NullTransaction transaction;

public delegate void CompletedEventHandler(object testParam);

internal class NullTransaction : ITransaction
{
public event CompletedEventHandler Completed;
    public void Dispose()
    {
        // no implementation
    }

    public void Complete()
    {
        // no implementation
    if(Completed != null)
            Completed.Invoke(this);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

油饼 2024-11-22 14:48:30

大多数事件不返回任何内容 - 事实上,您断言它没有返回类型。然后,您将自定义方法 (handler) 声明为返回 int,并尝试将其绑定到不返回 int 的委托。这行不通。

还;您的堆栈对于返回 int 无效,因为您“弹出”结果。

即我创建了一个测试

public event EventHandler SomeEvent;

并与其绑定;那么在这里:

Delegate dEmitted = handler.CreateDelegate(tDelegate);

您会发现 tDelegateEventHandler。这与返回 inthandler 不匹配。


重新堆栈(注释);考虑一下:

ilgen.Emit(OpCodes.Ldfld, counterFieldInfo); <=== should be ldsfld, by the way
ilgen.Emit(OpCodes.Ldc_I4, 1); // stack is now [counter] [1]
ilgen.Emit(OpCodes.Add); // stack is now [counter + 1]
ilgen.Emit(OpCodes.Pop); // stack is now empty
ilgen.Emit(OpCodes.Ret); // return

您已经加载了两个值,将它们相加,丢弃结果,然后返回。但您尚未返回您声明的 int - 这将导致 IL 检查失败。


如果您更改:

var handler =
    new DynamicMethod("CompletedHandler",
        null,
        GetDelegateParameterTypes(tDelegate),
        obj.GetType());

和:

ilgen.Emit(OpCodes.Ldsfld, counterFieldInfo);
ilgen.Emit(OpCodes.Ldc_I4_1);
ilgen.Emit(OpCodes.Add);
ilgen.Emit(OpCodes.Stsfld, counterFieldInfo);
ilgen.Emit(OpCodes.Ret);

那么它可能会按您的预期工作。

还;这更简单:

Delegate dEmitted = handler.CreateDelegate(tDelegate);
eventInfo.AddEventHandler(obj, dEmitted);

Most events don't return anything - in fact you assert that it has no return-type. You then declare your custom method (handler) as returning int, and try to bind it to a delegate that doesn't return an int. This won't work.

Also; your stack isn't valid for returning an int, since you "pop" the result.

i.e. I created a test with

public event EventHandler SomeEvent;

and bound to it; so then here:

Delegate dEmitted = handler.CreateDelegate(tDelegate);

you'll find that tDelegate is EventHandler. That doesn't match handler, which returns int.


Re the stack (comments); consider:

ilgen.Emit(OpCodes.Ldfld, counterFieldInfo); <=== should be ldsfld, by the way
ilgen.Emit(OpCodes.Ldc_I4, 1); // stack is now [counter] [1]
ilgen.Emit(OpCodes.Add); // stack is now [counter + 1]
ilgen.Emit(OpCodes.Pop); // stack is now empty
ilgen.Emit(OpCodes.Ret); // return

You've loaded two values, added them up, thrown the result away, and then returned. But you haven't returned the int that you claim to - this will fail IL inspection.


If you change:

var handler =
    new DynamicMethod("CompletedHandler",
        null,
        GetDelegateParameterTypes(tDelegate),
        obj.GetType());

and:

ilgen.Emit(OpCodes.Ldsfld, counterFieldInfo);
ilgen.Emit(OpCodes.Ldc_I4_1);
ilgen.Emit(OpCodes.Add);
ilgen.Emit(OpCodes.Stsfld, counterFieldInfo);
ilgen.Emit(OpCodes.Ret);

then it might work as you intend.

Also; this is simpler:

Delegate dEmitted = handler.CreateDelegate(tDelegate);
eventInfo.AddEventHandler(obj, dEmitted);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文