9

有人可以解释或指出为什么在下面的示例中没有发生运行时类型检查 - 字符串属性可以设置为任何类型值......
在非常意想不到的地方卡住了这个并且真的很惊讶

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

2 回答 2

2

我做了一个小实验:在你的类中添加了一个方法:

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

当然是做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.Stringnop重新编译ilasm.exe

现在使用错误类型参数调用 SetValue1 并产生与您的动态方法相同的结果。因此,在这种情况下,CLR 似乎没有进行类型检查。文档说:

在即时 (JIT) 编译期间,可选的验证过程会检查要被 JIT 编译为本机机器代码的方法的元数据和 Microsoft 中间语言 (MSIL),以验证它们是否是类型安全的。如果代码有权绕过验证,则跳过此过程。

在这种情况下,我们在本地机器上运行代码,因此 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

于 2014-06-17T15:40:10.760 回答
0

我认为这是因为您将参数声明为objectSystem.Object)。 intSystem.ValueType:System.ObjectA:System.ObjectSystem.Object是所有类的基类 ( http://msdn.microsoft.com/en-us/library/system.object.aspx )。例如,如果您更改typeof(object)typeof(string),您将收到强制转换错误。

编辑:我认为您的示例中禁用了参数类型检查,因为您替换了属性 getter/setter 的调用。如果您需要对调用动态方法进行类型检查,可以尝试使用下一个代码:

    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);
于 2013-07-31T14:17:09.557 回答