2013-07-31 19 views
9

Kann jemand erklären oder zeigen, warum Laufzeit Typen überprüfen nicht in Beispiel unten auftritt - String-Eigenschaft kann auf einen beliebigen Wert gesetzt werden ...
Stuck mit diesem in sehr unerwarteten Ort und war wirklich überraschtDynamicMethod und Typprüfungen

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

Antwort

0

Ich denke es ist, weil Sie Params als object (System.Object) deklarieren. int ist System.ValueType:System.Object und A:System.Object. System.Object ist die Basisklasse aller Klassen (http://msdn.microsoft.com/en-us/library/system.object.aspx). Wenn Sie zum Beispiel typeof(object) in typeof(string) ändern, erhalten Sie einen Cast-Fehler.

Edit: Ich denke, dass Params Typ Cheking in Ihrer Probe deaktiviert ist, weil Sie Aufruf der Eigenschaft Getter/Setter ersetzen. Wenn Sie eine Typprüfung für invoke dynamische Methode benötigen, können Sie versuchen nächsten Code zu verwenden:

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

Eigentlich war ich Typprüfung erwartet, wenn die Zuordnung eines gewissen Wert A.Name, nicht über die Eingabe Methodenparameter. pi.SetValue (a, 123) verursacht ArgumentException mit Text zum Objekttypkonvertierungsfehler, aber die SetValue-Methode akzeptiert auch Objekte als Parameter. –

+0

Tatsächlich tritt Referenzwechsel ohne Typenprüfung auf ... –

2

habe ich ein kleines Experiment: eine Methode, um Ihre Klasse Hinzugefügt:

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

Doing SetValue1(a, 123); wirft InvalidCastException Na sicher. Ich zerlegte dann den Code mit ildasm.exe. SetValue1 sieht wie folgt aus:

.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, lassen Sie uns das Guss castclass [mscorlib]System.String mit nop ersetzen und neu kompilieren mit ilasm.exe.

Jetzt wird der Aufruf von SetValue1 mit falschem Argument übergeben und erzeugt das gleiche Ergebnis wie Ihre dynamische Methode. Es scheint also, dass CLR in diesem Fall keine Typprüfung durchführt. Die documentation sagt:

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.

In diesem Fall werden wir Code auf dem lokalen Rechner ausgeführt wird, so dass der CLR vertraut darauf, dass die IL gültig ist.

Sie können die Assembly manuell überprüfen, indem Sie in der .exe-Ausgabedatei peverify.exe ausführen. Es wird mit einem Fehler zurück: Program::SetValue1][offset 0x00000004][found ref 'System.Object'][expected ref 'System.String'] Unexpected type on the stack.

Es ist ein sehr guter Beitrag, die dieses Thema erforscht: http://www.pcreview.co.uk/forums/net-type-safety-and-net-configuration-tool-t1225543.html

+0

Das ist richtig und ein guter Beweis. Das JIT hat keine grundlegenden Probleme mit nicht verifizierbarer IL. – usr