2009-08-30 3 views
5

Я работаю над подпрограммой, использующей DynamicMethod для извлечения значений из объекта. Он отлично работал с большинством типов данных, за исключением DateTime.Ticks, который является int64DynamicMethod возвращает неверное значение, когда тип свойства Int64

В следующем тестовом приложении. Я использую как MethodInfo, так и DynamicMethod, методInfo возвращает правильное значение, но DynamicMethod этого не делает. Есть идеи?

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; 
     } 
    } 
} 
+0

Не могли бы вы сообщить, каким образом результат неверен? –

+0

Пример неправильных значений: Получить по собственности 633871872000000000 Получить по MethodInfo 633871872000000000 Получить по DynamicMethod 3723350993856077580 –

+0

Спасибо Lasse за сообщение вашего результата. Первоначально я думал, что это вызвано боксом, поэтому я изменил подпись делегата и удалил код бокса, я не помог, я все равно получаю неправильное значение. – Tony

ответ

4

Вы генерируете недействительный код. Если обобщить полученный IL с ILASM

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

А затем запустить PEVerify на исполняемый файл, он скажет вам, что код неверен. (Вы не можете использовать callvirt для метода типа значения, подобного этому). Рабочий код должен выглядеть так:

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

Адаптируйте свое кодирование кода соответствующим образом и оно вернет правильное значение.

+0

Спасибо, что помогает. Добавлена ​​логика unboxing, и она отлично работает. il.Emit (OpCodes.Ldarg_0); // unboxing для значения типа if (pi.PropertyType.IsValueType) { il.Emit (OpCodes.Unbox, pi.ReflectedType); } il.EmitCall (OpCodes.Callvirt, mi, null); // бокс если (pi.PropertyType.IsValueType) { il.Emit (OpCodes.Box, pi.PropertyType); } – Tony