2013-07-31 11 views
9

Może ktoś wyjaśnić lub punkt do wyjaśnienia, dlaczego nie sprawdzić wykonawcza Typy występuje w próbce poniżej - ciąg właściwość można ustawić na dowolną wartość typu ...
zatrzymany z tego w bardzo nieoczekiwanym miejscu i był bardzo zaskoczonyDynamicMethod i rodzaj kontroli

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); 
} 

Odpowiedz

0

Myślę, że to dlatego, że deklarujesz parametry jako object (System.Object). int to System.ValueType:System.Object i A:System.Object. System.Object jest klasą podstawową wszystkich klas (http://msdn.microsoft.com/en-us/library/system.object.aspx). Jeśli na przykład zmienisz typeof(object) na typeof(string), otrzymasz błąd Cast.

Edytuj: Myślę, że cheking typu params jest wyłączony w twojej próbce, ponieważ zastępujesz wywołanie funkcji getter/setter. Jeśli potrzebujesz czek typu dla invoke metodą dynamiczną, można spróbować użyć następny kod:

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

Właściwie byłem spodziewałem czek typu podczas przypisywania do a.name jakąś wartość, a nie za pomocą parametrów metody typowania. pi.SetValue (a, 123) spowoduje ArgumentException z tekstem o błędzie konwersji typu obiektu, ale metoda SetValue przyjmuje również obiekty jako parametry. –

+0

Właściwie występuje zmiana odniesienia bez sprawdzania typów ... –

2

Zrobiłem mały eksperyment: Dodano metodę klasy:

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

Doing SetValue1(a, 123); rzuca InvalidCastException oczywiście. Następnie zdemontowałem kod przy użyciu ildasm.exe. SetValue1 wygląda następująco:

.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, niech zastąpi obsady castclass [mscorlib]System.String z nop i skompilować z ilasm.exe.

Teraz wywołanie SetValue1 z nieprawidłowym typem argumentu przechodzi i daje taki sam wynik, co metoda dynamiczna. Wydaje się więc, że CLR nie robi sprawdzania typu w tym przypadku. documentation mówi:

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.

W tym przypadku mamy uruchomiony kod na komputerze lokalnym, więc ufa CLR że IL jest prawidłowy.

Można ręcznie zweryfikować asembler, uruchamiając peverify.exe na wyjściowym pliku .exe. powróci z błędem: Program::SetValue1][offset 0x00000004][found ref 'System.Object'][expected ref 'System.String'] Unexpected type on the stack.

Jest to bardzo dobra wiadomość, która bada ten temat: http://www.pcreview.co.uk/forums/net-type-safety-and-net-configuration-tool-t1225543.html

+0

To jest poprawny i dobry dowód. JIT nie ma podstawowych problemów z niezweryfikowaną IL. – usr