2013-07-31 2 views
9

Может кто-нибудь объяснить или указать объяснение, почему проверка типов выполнения не происходит в примере ниже - свойство string может быть установлено на любое значение типа ...
Застрял с этим в очень неожиданном месте и был очень удивленDynamicMethod и проверки типов

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

ответ

0

Я думаю, что это потому, что вы объявляете параметры как object (System.Object). int является System.ValueType:System.Object и A:System.Object. System.Object - базовый класс всех классов (http://msdn.microsoft.com/en-us/library/system.object.aspx). Если вы измените typeof(object) на typeof(string), например, вы получите ошибку при произнесении.

Редактировать: Я думаю, что в вашем примере отключен тип cheking, потому что вы заменяете вызов свойства getter/setter. Если вам нужна проверка типа для Invoke метода динамического, вы можете попробовать использовать следующий код:

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

На самом деле я был ожидаемый тип проверки при назначении a.name какое-то значение, а не с помощью параметров метода ввода. pi.SetValue (a, 123) вызовет ArgumentException с текстом об ошибке преобразования типа объекта, но метод SetValue также принимает объекты как параметры. –

+0

Фактически происходит смена ссылки без проверки типов ... –

2

Я сделал небольшой эксперимент: добавлен метод в класс:

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

Doing SetValue1(a, 123); бросает InvalidCastException конечно. Затем я разобрал код, используя ildasm.exe. SetValue1 выглядит следующим образом:

.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 

Хорошо, давайте заменим бросание castclass [mscorlib]System.String с nop и перекомпилировать с ilasm.exe.

Теперь вызов SetValue1 с неправильным аргументом типа проходит и дает тот же результат, что и ваш динамический метод. Таким образом, похоже, что CLR в этом случае не выполняет проверку типов. documentation говорит:

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.

В этом случае мы проводим код на локальном компьютере, так что CLR доверяет, что IL является действительным.

Вы можете вручную проверить сборку, выполнив peverify.exe в выходном .exe-файле. Он вернется с ошибкой: Program::SetValue1][offset 0x00000004][found ref 'System.Object'][expected ref 'System.String'] Unexpected type on the stack.

Существует очень хороший пост, который исследует эту тему: http://www.pcreview.co.uk/forums/net-type-safety-and-net-configuration-tool-t1225543.html

+0

Это правильно и хорошее доказательство. У JIT нет фундаментальных проблем с непроверяемым IL. – usr