Exception from DynamicMethod.CreateDelegate, almost identical MSDN example
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);
精彩评论