views:

191

answers:

1

I'm working on a routine to use DynamicMethod to retrieve values from a object. It worked fine with most of data types, except for DateTime.Ticks, which is int64

In the following test app. I uses both MethodInfo and DynamicMethod, the methodInfo returns correct value but DynamicMethod doesn't. Any ideas?

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication2
{
    public delegate object MemberGetDelegate(object obj);

    class Program
    {
        static void Main(string[] args)
        {
            DateTime dat = DateTime.Today;
            PropertyInfo pi = typeof(DateTime).GetProperty("Ticks");
            MethodInfo mi = pi.GetGetMethod();
            Type type = pi.PropertyType;
            object ticks = mi.Invoke(dat, new object[] { });
            Console.WriteLine("Get by MethodInfo " + ticks.ToString());

            MemberGetDelegate mget=TypeUtils.GetMemberFunc(pi);
            object ret = mget(dat);
            Console.WriteLine("Get by DynamicMethod " + ret.ToString());

            Console.Read();
        }
    }

    static class TypeUtils
    {
        public static readonly Type objectType = typeof(object);
        public static readonly Type[] typeArray = new[] { typeof(object) };

        public static MemberGetDelegate GetMemberFunc(PropertyInfo pi)
        {

            MethodInfo mi = pi.GetGetMethod();

            if (mi != null)
            {
                DynamicMethod dm = new DynamicMethod("_" + mi.Name,
                                                     objectType,
                                                     typeArray,
                                                     pi.Module, true);
                ILGenerator il = dm.GetILGenerator();

                // Load the instance of the object (argument 0) onto the stack
                il.Emit(OpCodes.Ldarg_0);

                // Call underlying get method
                il.EmitCall(OpCodes.Callvirt, mi, null);

                //boxing
                if (pi.PropertyType.IsValueType)
                {
                    il.Emit(OpCodes.Box, pi.PropertyType);                   
                }

                // return the value on the top of the stack
                il.Emit(OpCodes.Ret);

                return  (MemberGetDelegate) dm.CreateDelegate(typeof (MemberGetDelegate));

            }
            return null;
        }
    }
}
+1  A: 

You're generating invalid code. If you compile the resulting IL with Ilasm

ldarg.0
callvirt instance int64 [mscorlib]System.DateTime::get_Ticks()
box int64
ret

And then run PEVerify on the executable, it will tell you that the code is invalid. (You can't use callvirt on a value type method like that). Working code should look like this

ldarg.0
unbox [mscorlib]System.DateTime
call instance int64 [mscorlib]System.DateTime::get_Ticks()
box int64
ret

Adapt your code generation accordingly and it will return the correct value.

Mattias S
Thanks, that helps. Added unboxing logic and it worked fine. il.Emit(OpCodes.Ldarg_0); // unboxing for value type if (pi.PropertyType.IsValueType) { il.Emit(OpCodes.Unbox,pi.ReflectedType); } il.EmitCall(OpCodes.Callvirt, mi, null); //boxing if (pi.PropertyType.IsValueType) { il.Emit(OpCodes.Box, pi.PropertyType); }
Tony