我发现我在生产的DynamicMethod中获得“操作可能会破坏运行时的稳定性”的原因,虽然我很容易修复它,但它给我留下了一个看似简单的问题:
以下是示例程序。运行它时,它会在编译方法时因“操作可能使运行时不稳定”异常而崩溃。
通过仅更改声明为TestClass
类型的变量的类型而不是Object
来解决问题,但我仍然想知道如何将引用转换为适当的类型在代码中。
在代码中,我用星号标记了一行。那时我可以发出什么代码来将堆栈上的Object
引用改为TestClass
引用,以便方法调用将通过?
请注意,我知道是产生问题的方法调用,如果我完全注释掉这些行,那么变量的类型无关紧要,编译方法并执行正常。
这是代码。
using System;
using System.Reflection.Emit;
namespace ConsoleApplication9
{
public class TestClass
{
public void TestMethod() { }
}
class Program
{
static void Main(string[] args)
{
Type type = typeof(TestClass);
DynamicMethod method = new DynamicMethod("", typeof(Object), null);
ILGenerator il = method.GetILGenerator();
LocalBuilder variable = il.DeclareLocal(typeof(Object));
// Construct object
il.Emit(OpCodes.Newobj, type.GetConstructor(new Type[0]));
il.Emit(OpCodes.Stloc, variable);
// Call Test method
il.Emit(OpCodes.Ldloc, variable);
// ***************************************** what do I do here?
il.Emit(OpCodes.Call, type.GetMethod("TestMethod"));
// Return object
il.Emit(OpCodes.Ldloc, variable);
il.Emit(OpCodes.Ret);
// Create and call delegate
Func<Object> fn = (Func<Object>)method.CreateDelegate(
typeof(Func<Object>));
Object instance = fn();
}
}
}
答案 0 :(得分:11)
答案简短:
// Call Test method
il.Emit(OpCodes.Ldloc, variable);
il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Call, type.GetMethod("TestMethod"));
虽然怎么来?好吧,我使用的方法是Reflector。首先,编写一个执行您想要执行的操作的方法。我想出了以下内容:
private static object PrecompiledTest()
{
object variable = new TestClass();
((TestClass) variable).TestMethod();
return variable;
}
现在,编译它,打开Reflector,然后打开你的程序集。导航到您的函数并查看MSIL。上面的函数反编译为:
.method private hidebysig static object PrecompiledTest() cil managed
{
.maxstack 1
.locals init (
[0] object variable,
[1] object CS$1$0000)
L_0000: nop
L_0001: newobj instance void EmitTest.TestClass::.ctor()
L_0006: stloc.0
L_0007: ldloc.0
L_0008: castclass EmitTest.TestClass
L_000d: callvirt instance void EmitTest.TestClass::TestMethod()
L_0012: nop
L_0013: ldloc.0
L_0014: stloc.1
L_0015: br.s L_0017
L_0017: ldloc.1
L_0018: ret
}
以上使用callvirt
代替call
。我并不是那么精通IL,所以我不确定区别,但call
在你的例子中起作用。最后一件事,我们谈论的是Reflector的主题。您可以使用ReflectionEmitLanguage插件效果很好,为您生成Emit
代码。此插件为您生成以下代码:
public MethodBuilder BuildMethodPrecompiledTest(TypeBuilder type)
{
// Declaring method builder
// Method attributes
System.Reflection.MethodAttributes methodAttributes =
System.Reflection.MethodAttributes.Private
| System.Reflection.MethodAttributes.HideBySig
| System.Reflection.MethodAttributes.Static;
MethodBuilder method = type.DefineMethod("PrecompiledTest", methodAttributes);
// Preparing Reflection instances
ConstructorInfo ctor1 = typeof(TestClass).GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null,
new Type[]{
},
null
);
MethodInfo method2 = typeof(TestClass).GetMethod(
"TestMethod",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null,
new Type[]{
},
null
);
// Setting return type
method.SetReturnType(typeof(Object));
// Adding parameters
ILGenerator gen = method.GetILGenerator();
// Preparing locals
LocalBuilder variable = gen.DeclareLocal(typeof(Object));
LocalBuilder CS$1$0000 = gen.DeclareLocal(typeof(Object));
// Preparing labels
Label label23 = gen.DefineLabel();
// Writing body
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Newobj,ctor1);
gen.Emit(OpCodes.Stloc_0);
gen.Emit(OpCodes.Ldloc_0);
gen.Emit(OpCodes.Castclass,TestClass);
gen.Emit(OpCodes.Callvirt,method2);
gen.Emit(OpCodes.Nop);
gen.Emit(OpCodes.Ldloc_0);
gen.Emit(OpCodes.Stloc_1);
gen.Emit(OpCodes.Br_S,label23);
gen.MarkLabel(label23);
gen.Emit(OpCodes.Ldloc_1);
gen.Emit(OpCodes.Ret);
// finished
return method;
}