开发者

Exception from DynamicMethod.CreateDelegate, almost identical MSDN example

开发者 https://www.devze.com 2023-03-13 15:06 出处:网络
When i call CreateDelegate(delegateType) i get a System.ArgumentException, which a开发者_JAVA百科ccording to MSDN is because the delegateType has the wrong number of parameters or the wrong parameter

When i call CreateDelegate(delegateType) i get a System.ArgumentException, which a开发者_JAVA百科ccording 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);
    }
}


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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号