代码之家  ›  专栏  ›  技术社区  ›  Michael B

为什么一个调用指令会导致运行时降级?

  •  1
  • Michael B  · 技术社区  · 15 年前

    下面的代码生成一个异常,表示它可能会使运行时降级。

    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 至操作码.CallVirt解决了问题。 这一切都很好,但我可以使用typebuilder创建一个动态类型,该类型具有使用(MethodBuilder)生成的静态方法,该方法具有 确切地 相同的IL,然后使用CreateDelegate,它不会引发此异常。实际上,使用methodbuilder,您可以对完全不同的对象的虚拟方法进行调用,而不是类型继承的方法。

    如果我做了这样的事 new object().ToString() 它还发出 call 向IL发出指令,而不是 callvirt .

    如果使用 除了在实例重写中调用基方法的上下文之外,那么为什么CLR允许在不同类型上静态生成这样的方法呢?或允许 呼叫 针对已知的非空实体发出的指令?

    或者这仅仅是通过 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 回复  |  直到 15 年前
        1
  •  1
  •   Stewart    15 年前

    它试图保护你不做错事。这里的调用是错误的,因为无法知道对象的具体类型。合法地知道对象不能是它所看到的以外的任何东西(例如它看到一个密封的类,或者它只是实例化了对象)的编译器可以逃脱,因为它知道它是安全的,但是在您的例子中,您所看到的只是作为参数传递的“object”,所以这个转换是不安全的。在“new object().ToString()”的情况下,对象的类型是绝对具体的对象,因此不需要虚拟调用,但是在您的情况下,对象作为参数出现,因此您完全无法知道它的具体类型是什么。不过,你不应该太烦恼。JITer可能比您更了解如何JIT这段代码,并且可能会将callvirt转换成直接调用,但是它也可以反过来,将调用转换成间接调用,因此实际上没有什么需要担心的。