Delegate Called via System.Reflection.Emit Returning Wrong Value


J

Jehu Galeahsa

Hello:

I have a class called MemberSetup that looks like this:

public class MemberSetup<T> : IMemberSetup
{
private static readonly Func<T> _default = () => default(T);
private MethodInfo _method;
private object _target;

internal MemberSetup()
{
}

public MemberSetup<T> Returns(T value)
{
Func<T> func = () => value;
_method = func.Method;
_target = func.Target;
return this;
}

public MemberSetup<T> Throws(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException("exception", "The
exception must not be null.");
}
Func<T> func = () => { throw exception; };
_method = func.Method;
_target = func.Target;
return this;
}

MethodInfo IMemberSetup.Method
{
get { return _method ?? _default.Method; }
}

object IMemberSetup.Target
{
get { return _target ?? _default.Target; }
}
}

It allows someone to specify the action to take at a later date. I use
the MethodInfo and the Target properties to call the appropriate
delegate later. If a return value is specified, that is returned. If a
throw is specified, it is thrown. Otherwise, default(T) is returned.

At some point, I use System.Reflection.Emit to copy this MemberSetup
to a backing field in a dynamically generated class. This generated
class is responsible for invoking the delegate. Here is the code for
invoking the delegate:

ILGenerator bodyGenerator = methodBuilder.GetILGenerator();
IMemberSetup methodSetup;
if (_members.TryGetValue(methodInfo, out methodSetup))
{
bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldfld, fieldMappings[methodSetup]);
bodyGenerator.Emit(OpCodes.Callvirt,
typeof(IMemberSetup).GetProperty("Method").GetGetMethod());

bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldfld, fieldMappings[methodSetup]);
bodyGenerator.Emit(OpCodes.Callvirt,
typeof(IMemberSetup).GetProperty("Target").GetGetMethod());

bodyGenerator.Emit(OpCodes.Ldnull);

bodyGenerator.Emit(OpCodes.Callvirt,
typeof(MethodInfo).GetMethod("Invoke", new Type[]
{ typeof(object), typeof(object[]) }));
bodyGenerator.Emit(OpCodes.Ret);
}

Just assume that I am creating my MethodBuilder correctly and that
fieldMappings[methodSetup] gets me the correct backing field (type of
IMemberSetup).

The problem I am running into is that if I tell the MemberSetup to
return, say, 123, I will get some arbitrary value (it changes with
each run). It almost looks like a memory address. I am not sure what
is happening to my return value. Now, if I try to call this exact same
code directly without Emit, it works as expected. When I debug the
code, the delegate will get hit and return the expected value. So
somewhere between the delegate returning the value and it being
returned by the Emit'd method it gets mangled. On the other hand, if
the return type is a reference type, this works as expected; so it has
something to do with primitive types.

I was thinking this might have something to do with boxing and
unboxing. I'm not sure. I can provide a complete example, if needed. I
was hoping someone might have run into this issue before and knew what
the cause was.

FYI, I am playing around with a very simple Mock object framework, and
being able to call arbitrary delegates is one of the next tasks to
complete. I've been struggling with this for about a week without much
progress. Any help would be greatly appreciated.

Thanks,
Travis Parks
 
Ad

Advertisements

J

Jehu Galeahsa

I think I may have solved this one on my own. Turns out it did have to
do with boxing. I made two simple changes to my code. First, I
eliminated the Method and Target properties and now just have a method
called Call. This avoid an unnecessary reflective call and requires a
lot less emit code. Secondly, I check if the type is a value type and
emit Unbox_Any. This appears to resolve my issue. Here is the slightly
modified code:

public class MemberSetup<T> : IMemberSetup
{
private static readonly Func<T> _default = () => default(T);
private Func<T> _delegate;

internal MemberSetup()
{
}

public MemberSetup<T> Returns(T value)
{
Func<T> func = () => value;
_delegate = func;
return this;
}

public MemberSetup<T> Throws(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException("exception", "The
exception must not be null.");
}
Func<T> func = () => { throw exception; };
_delegate = func;
return this;
}

object IMemberSetup.Call()
{
return (_delegate ?? _default)();
}
}
....
ILGenerator bodyGenerator = methodBuilder.GetILGenerator();
IMemberSetup methodSetup;
if (_members.TryGetValue(methodInfo, out methodSetup))
{
bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldfld, fieldMappings[methodSetup]);
bodyGenerator.Emit(OpCodes.Callvirt,
typeof(IMemberSetup).GetMethod("Call"));
if (methodInfo.ReturnType.IsValueType)
{
bodyGenerator.Emit(OpCodes.Unbox_Any, methodInfo.ReturnType);
}
bodyGenerator.Emit(OpCodes.Ret);
}

I think that is pretty clean. I might need to go back the Method and
Target properties in the future if I want to allow for parameters.
I'll get there. For now, I need a way to avoid exceptions being
wrapped in TargetInvocationExceptions. That's my next battle.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top