test
Recently I had one of those aggrivating, head pounding, pick up the
monitor and throw it through the window, wall, co-worker, whatever is
closest. The issue involved using DynamicMethods,
CreateDelegate method, and binding the DynamicMethod to an object.
I was working on an experiment with DynamicMethods and
Generics (thats for another post), when I encountered the problem.
Thinking it was some issue to do with the Generics (which
there are a lot), I worked on a test code that had nothing to do with
Generics and still ran into the problem.
Now according to the .Net documentation the first parameter of the
DynamicMethod, when used in CreateDelegate bound to an object, has to
be the same type as the object to which the method is being
bound.
Parameters for overloaded CreateDelegate method of DynamicMethod:
delegateType
A delegate type whose
signature matches that of the dynamic method, minus the first parameter.
target
An object the delegate is
bound to. Must be of the same type as the first parameter of the
dynamic method.
Now here is code similar to what i was working on.
public delegate void TestingDelegate(Form instance, int value);
public class BadGenerator
{
public Delegate Generate(Form instance)
{
Type[] parameters = new Type[2];
parameters[0]
= typeof(Form);
parameters[1]
= typeof(int);
DynamicMethod dm = new DynamicMethod("", typeof(void), parameters, typeof(Form));
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ret);
return dm.CreateDelegate(typeof(TestingDelegate), instance);
}
}
Now I imagine that some of the more observent of you have already
noticed my simple but stupid mistake. Whenever I ran ithis
code, it
would throw an exception when it tries to execute the CreateDelegate
function. It would always throw System.ArgumentException
"Error
binding to target method." Not exactly the most descriptive
error
message. I tiried lots of things to get it to work, even used
Reflector to trace the calls (no luck, it ends up calling some Win32
APIs). I spent too much time trying to figure this problem
out,
without any luck. Then I happened to be rereading the
documentation,
for the tenth time it seemed, when I saw this in the Remarks about the
CreateDelegate function:
This method overload requires target
to be of the same type as the first parameter of the dynamic method, or
to be assignable to that type (for example, a derived class). The signature of delegateType has
all the parameters of the dynamic method except the first.
For example, if the dynamic method has the parameters String, Int32,
and Byte, then delegateType has the parameters Int32 and Byte; target
is of type String.
I've
bolded the most important sentence. When I read that, I
felt like an idiot, a very very very big idiot.
The corrected code would be:
public delegate void TestingDelegate(int value);
public class BadGenerator
{
public Delegate Generate(Form instance)
{
Type[] parameters = new Type[2];
parameters[0]
= typeof(Form);
parameters[1]
= typeof(int);
DynamicMethod dm = new DynamicMethod("", typeof(void), parameters, typeof(Form));
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ret);
return dm.CreateDelegate(typeof(TestingDelegate), instance);
}
}
I had to change the delegate from (
public delegate void TestingDelegate(Form instance, int value); ) to (
public delegate void TestingDelegate(int value); ), because a
DynamicMethod bound to an object always takes one more parameter than
the delegate it is referencing. The other solution would be
to define another parameter for the DynamicMethod, so the parameter
list would be (Form, Form, int).
So what is the moral of the story:
Always Read The Documentation,
Completely.