【问题标题】:Exception from DynamicMethod.CreateDelegate, almost identical MSDN exampleDynamicMethod.CreateDelegate 的异常,几乎相同的 MSDN 示例
【发布时间】:2011-06-16 11:00:26
【问题描述】:

当我调用 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 });

    ...

如您所见,cmets 甚至在那里。如您所见,我有 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);
    }
}

【问题讨论】:

  • 你能显示你试图绑定的事件吗?即与事件有关的事情称为eventName?另外,请注意,您当前没有存储递增的计数器
  • 我在帖子中添加了一些代码。存储是什么意思?我在方法之外有声明。
  • 通过我引用的更改以及您的 Completed 事件,我让它工作并增加计数器

标签: c# reflection delegates dynamicmethod


【解决方案1】:

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

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

即我用

创建了一个测试
public event EventHandler SomeEvent;

并绑定到它;那么这里:

Delegate dEmitted = handler.CreateDelegate(tDelegate);

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


重新堆栈(cmets);考虑:

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

【讨论】:

  • SO 的成员仍然让我感到惊讶。谢谢你。尽管我得到了一个新的例外:FieldAccessException 除非我将我的 eventCounter 更改为公共字段而不是私有字段(要明确,当我将它作为公共字段时它可以工作)。有什么建议吗?
  • @Erik - 当然;将new DynamicMethod 调用中的obj.GetType() 更改为声明counterFieldInfo 的类型; owner 的意思是“假装我在这堂课上,就我被允许访问的内容而言”。您也可以跳过现场可见性检查。
  • 没关系,我解决了。动态方法所有者是引发事件的对象。我把它放在私有字段所在的班级。
  • @Erik 很确定我刚刚说过;p
  • 啊,没看到你已经回答了。还是谢谢!
猜你喜欢
  • 2015-08-03
  • 1970-01-01
  • 2015-01-12
  • 1970-01-01
  • 1970-01-01
  • 2019-04-21
  • 1970-01-01
  • 1970-01-01
  • 2013-02-26
相关资源
最近更新 更多