2013-07-31 14 views
9

Có thể ai đó giải thích hoặc giải thích tại sao thời gian kiểm tra kiểu không xảy ra trong mẫu dưới đây - chuỗi tài sản có thể được đặt thành bất kỳ giá trị ...
Bị kẹt với điều này ở nơi rất bất ngờ và thực sự ngạc nhiênDynamicMethod và loại kiểm tra

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

namespace Dynamics 
{ 
internal class Program 
    { 
    private static void Main(string[] args) 
    { 
     var a = new A(); 
     a.Name = "Name"; 
     Console.WriteLine(a.Name.GetType().Name); 

     PropertyInfo pi = a.GetType().GetProperty("Name");   

     DynamicMethod method = new DynamicMethod(
       "DynamicSetValue", // NAME 
       null, // return type 
       new Type[] 
          { 
           typeof(object), // 0, objSource 
           typeof(object), // 1, value 
          }, // parameter types 
       typeof(Program), // owner 
       true); // skip visibility 

     ILGenerator gen = method.GetILGenerator(); 
     gen.Emit(OpCodes.Ldarg_0); 
     gen.Emit(OpCodes.Ldarg_1); 
     gen.Emit(OpCodes.Call, pi.GetSetMethod(true)); 
     gen.Emit(OpCodes.Ret); 

     SetValue setMethod = (SetValue)method.CreateDelegate(typeof(SetValue)); 

     int val = 123; 
     setMethod(a, val); 
     Console.WriteLine(a.Name.GetType().Name); 

     A anotherA = new A(); 
     anotherA.Name = "Another A"; 
     setMethod(a, anotherA); 
     Console.WriteLine(a.Name.GetType().Name); 
    } 
} 

public class A 
{ 
    public string Name { get; set; } 
} 

public delegate void SetValue(object obj, object val); 
} 

Trả lời

0

Tôi nghĩ là vì bạn khai báo thông số là object (System.Object). intSystem.ValueType:System.ObjectA:System.Object. System.Object là lớp cơ sở của tất cả các lớp (http://msdn.microsoft.com/en-us/library/system.object.aspx). Nếu bạn thay đổi typeof(object) thành typeof(string) chẳng hạn, bạn sẽ gặp phải lỗi truyền.

Chỉnh sửa: Tôi nghĩ rằng kiểu params cheking bị vô hiệu hóa trong mẫu của bạn bởi vì bạn thay thế cuộc gọi getter/setter thuộc tính. Nếu bạn cần một tấm séc kiểu cho phương pháp năng động invoke, bạn có thể thử sử dụng mã tiếp theo:

var a = new A(); 
    a.Name = "Name"; 
    Console.WriteLine(a.Name.GetType().Name); 

    PropertyInfo pi = a.GetType().GetProperty("Name");   

    DynamicMethod method = new DynamicMethod(
      "DynamicSetValue", // NAME 
      null, // return type 
      new Type[] 
         { 
          typeof(Object), // 0, objSource 
          pi.PropertyType, // 1, value 
         }, // parameter types 
      typeof(OracleUserOlapRepositoryTests), // owner 
      true); // skip visibility 

    ILGenerator gen = method.GetILGenerator(); 
    gen.Emit(OpCodes.Ldarg_0); 
    gen.Emit(OpCodes.Ldarg_1); 
    gen.Emit(OpCodes.Call, pi.GetSetMethod(true)); 
    gen.Emit(OpCodes.Ret); 

    //correct 
    method.Invoke(a, new object[]{a,"test"}); 

    //error 
    method.Invoke(a, new object[]{a,new List<String>()}); 

    Console.WriteLine(a.Name.GetType().Name); 
+0

Thật sự tôi đã được dự kiến ​​loại kiểm tra khi giao đến A.Name một số giá trị, chứ không phải thông qua các tham số kiểu gõ. pi.SetValue (a, 123) sẽ gây ra ArgumentException với văn bản về lỗi chuyển đổi kiểu đối tượng, nhưng phương thức SetValue cũng chấp nhận các đối tượng làm tham số. –

+0

Trên thực tế xảy ra thay đổi tham chiếu mà không cần kiểm tra loại ... –

2

Tôi đã làm một thí nghiệm nhỏ: Thêm một phương pháp để lớp học của bạn:

static void SetValue1(A a, object v) 
    { 
     a.Name = (string)v; 
    } 

Làm SetValue1(a, 123); ném InvalidCastException tất nhiên. Sau đó, tôi đã tháo mã bằng cách sử dụng ildasm.exe. SetValue1 trông như thế này:

.method private hidebysig static void SetValue1(class ConsoleApplication2.A a, 
                object v) cil managed 
    { 
    // Code size  15 (0xf) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldarg.1 
    IL_0003: castclass [mscorlib]System.String // <--- replace this with nop 
    IL_0008: callvirt instance void ConsoleApplication2.A::set_Name(string) 
    IL_000d: nop 
    IL_000e: ret 
    } // end of method Program::SetValue1 

Ok, chúng ta hãy thay thế các diễn viên castclass [mscorlib]System.String với nop và biên dịch lại với ilasm.exe.

Bây giờ, cuộc gọi tới SetValue1 với đối số loại sai sẽ chuyển và tạo ra kết quả tương tự như phương pháp động của bạn. Vì vậy, có vẻ như CLR không làm kiểm tra kiểu trong trường hợp này. Các documentation nói:

During just-in-time (JIT) compilation, an optional verification process examines the metadata and Microsoft intermediate language (MSIL) of a method to be JIT-compiled into native machine code to verify that they are type safe. This process is skipped if the code has permission to bypass verification.

Trong trường hợp này, chúng tôi đang chạy mã trên máy tính cục bộ, do đó tin tưởng CLR rằng IL là hợp lệ.

Bạn có thể xác minh thủ công assembly bằng cách chạy peverify.exe trên tệp .exe đầu ra. Nó sẽ trở lại với một lỗi: Program::SetValue1][offset 0x00000004][found ref 'System.Object'][expected ref 'System.String'] Unexpected type on the stack.

Có một bài rất tốt mà khám phá chủ đề này: http://www.pcreview.co.uk/forums/net-type-safety-and-net-configuration-tool-t1225543.html

+0

Điều này là chính xác và là một bằng chứng tốt. JIT không có vấn đề cơ bản với IL không thể xác minh. – usr