2010-11-20 108 views
1

以下代码会产生一个异常,指出它可能会使运行时不稳定。为什么调用指令会导致运行时不稳定?

var method = new DynamicMethod("CallObjectToString", typeof(string),new[]{ typeof(object)}); 
var ilgen =method.GetILGenerator(); 
ilgen.Emit(OpCodes.Ldarg_0); 
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString")); 
ilgen.Emit(OpCodes.Ret); 
var @delegate = method.CreateDelegate(typeof(Func<object, string>)) as Func<object,string>; 
@delegate(new object()); 

Opcodes.Call更改为Opcodes.CallVirt修复了问题。 这一切都很好,但我可以使用typebuilder创建一个动态类型,它具有使用(MethodBuilder)构建的静态方法,它具有相同的IL,并且使用CreateDelegate,并且它不会抛出此异常。实际上,使用方法构建器可以使某个对象完全不同的对象的虚拟方法进行调用,甚至不需要该对象的类型继承。

更令人费解的是,如果我执行诸如new object().ToString()之类的操作,它还会向IL发出call指令,而不是callvirt

如果使用call调用虚拟方法是非法的,除非在实例重写中调用基方法的上下文中,那么为什么CLR允许静态地使用不同类型的方法?或者允许call指令针对已知的非空实体发出?

或者这是通过DynamicMethod生成的代码的唯一问题?

编辑: 我从性能角度不问,因为我真的不关心它。下面的代码虽然稍微涉及创建一个委托,执行不稳定行动而不破坏运行时。这是整个问题。为什么一个是合法的,另一个是非法的?

var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("SomeAssembly"), AssemblyBuilderAccess.Run) ; 
var moduleBuilder = assemblyBuilder.DefineDynamicModule("SomeModule"); 
var typeBuilder = moduleBuilder.DefineType("SomeType"); 
var methodBuilder = typeBuilder.DefineMethod("SomeStaticMethod",MethodAttributes.Public | MethodAttributes.Static,typeof(string),new[]{typeof(object)}); 
var ilgen = methodBuilder.GetILGenerator(); 
ilgen.Emit(OpCodes.Ldarg_0); 
ilgen.Emit(OpCodes.Call, typeof(object).GetMethod("ToString")); 
ilgen.Emit(OpCodes.Ret); 
var newType = typeBuilder.CreateType(); 
var @delegate = Delegate.CreateDelegate(typeof(Func<object, string>),newType.GetMethod("SomeStaticMethod")) as Func<object,string>; 
@delegate(new object()); 

回答

1

它试图保护你免于做错事。这里的调用是错误的,因为对象的具体类型不能被知道。一个编译器在法律上知道对象不能是除了它所看到的以外的任何东西(例如它看到一个密封的类,或者它只是实例化对象)可以逃脱,因为它知道它是安全的,但在你的情况下,你看到的全部是“对象”作为参数传递,所以这种转换是不安全的。在“new object()。ToString()”的情况下,对象的类型被绝对具体地称为对象,因此不需要虚拟调用,但是在您的情况下,对象作为参数进来你完全无法知道它的具体类型是什么。你不应该太担心。 JITer甚至可能比你在处理JIT这个代码的时候知道的更多,并且可能会将callvirt变成一个直接的调用,但是它又可以做相反的事情,并且迫使这个调用变成间接调用,所以真的很几乎不用担心。

+0

这是真的,但为什么它允许在代码中,我通过与typebuilder反射发射生成的代码。它实际上并没有调用对象的重载,但实际上是直接调用的。 – 2010-11-20 20:47:47

+0

我只能说,看来Reflection.Emit假设你知道你在做什么。我不明白这里直接的电话会不会以任何严肃的方式破坏运行时间,但看起来DynamicMethod首先试图保护你。这很难确定。 – Stewart 2010-11-20 20:53:09

相关问题