以下代码产生一个异常,表明它可能会使运行时不稳定。
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,它不会抛出这个例外。事实上,使用methodbuilder,你可以让某些东西对一个完全不同的对象的虚拟方法进行调用,甚至不是那个类型继承的虚拟方法。
更令人费解的是,如果我执行类似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());
答案 0 :(得分:1)
它试图保护你不要做错事。这里的调用是错误的,因为无法知道对象的具体类型。一个合法的知道对象不能是它看到的东西(例如它看到一个密封的类,或者它只是实例化了对象)的编译器可以逃脱,因为它知道它是安全的,但在你的情况下,所有你看到的是作为参数传递的“对象”,因此这种转换不安全。在“new object()。ToString()”的情况下,对象的类型绝对具体地称为对象,因此不需要虚拟调用,但是在您的情况下,对象作为参数进入,所以你完全没办法知道它的具体类型是什么。你不应该担心太多。对于JIT这个代码,JITer可能知道甚至比你更好,并且可能会使callvirt成为一个直接调用,但是然后它可以反过来强制执行一个间接调用,所以真的非常不用担心。