有人可以解释或指出为什么运行时类型检查不会出现在下面的示例中 - 字符串属性可以设置为任何类型值...
在非常意想不到的地方坚持这个并且真的很惊讶
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 :(得分: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.String
替换为nop
,然后使用ilasm.exe
重新编译。
现在,使用错误类型参数调用SetValue1会传递并产生与动态方法相同的结果。所以在这种情况下CLR似乎没有进行类型检查。 documentation说:
在实时(JIT)编译期间,可选的验证过程会检查要将JIT编译为本机机器代码的方法的元数据和Microsoft中间语言(MSIL),以验证它们是否类型安全。如果代码有权绕过验证,则会跳过此过程。
在这种情况下,我们在本地计算机上运行代码,因此CLR信任IL有效。
您可以通过在输出.exe文件上运行peverify.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
答案 1 :(得分:0)
我认为是因为你将params声明为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)
,则会出现施法错误。
编辑: 我认为你的样本中禁用了params类型的cheking,因为你替换了属性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);