2009-08-30 10 views
5

Tôi đang làm việc trên một thường trình để sử dụng DynamicMethod để truy xuất các giá trị từ một đối tượng. Nó hoạt động tốt với hầu hết các kiểu dữ liệu, ngoại trừ DateTime.Ticks, là int64DynamicMethod trả về giá trị không chính xác khi loại thuộc tính là Int64

Trong ứng dụng thử nghiệm sau đây. Tôi sử dụng cả hai MethodInfo và DynamicMethod, methodInfo trả về giá trị đúng nhưng DynamicMethod thì không. Bất kỳ ý tưởng?

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

Bạn có thể vui lòng báo cáo theo kết quả không chính xác mà bạn nhận được không? –

+0

Ví dụ về các giá trị không đúng: Nhận theo thuộc tính 633871872000000000 Lấy bằng phương phápInfo 633871872000000000 Nhận bởi DynamicMethod 3723350993856077580 –

+0

Cảm ơn Lasse đã đăng kết quả của bạn. Ban đầu tôi nghĩ rằng nó đã được gây ra bởi boxing, vì vậy tôi đã thay đổi chữ ký của đại biểu và loại bỏ mã đấm bốc, tôi đã không giúp đỡ, tôi vẫn nhận được giá trị không chính xác. – Tony

Trả lời

4

Bạn đang tạo mã không hợp lệ. Nếu bạn biên dịch kết quả IL với ILAsm

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

Và sau đó chạy PEVerify trên thực thi, nó sẽ cho bạn biết rằng mã không hợp lệ. (Bạn không thể sử dụng callvirt trên một phương thức kiểu giá trị như thế). Mã làm việc sẽ giống như thế này

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

Điều chỉnh việc tạo mã cho phù hợp và nó sẽ trả lại giá trị chính xác.

+0

Cảm ơn, điều đó giúp ích. Đã thêm logic unboxing và nó hoạt động tốt. il.Emit (OpCodes.Ldarg_0); // unboxing cho loại giá trị nếu (pi.PropertyType.IsValueType) { il.Emit (OpCodes.Unbox, pi.ReflectedType); } il.EmitCall (OpCodes.Callvirt, mi, null); // boxing nếu (pi.PropertyType.IsValueType) { il.Emit (OpCodes.Box, pi.PropertyType); } – Tony