From: Jehu Galeahsa on 7 May 2010 22:57 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
From: Jehu Galeahsa on 7 May 2010 23:14 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.
|
Pages: 1 Prev: how to clear keyboard buffer? Next: Handling Roles end-to-end |