将对象转换为 IL 中的特定类?
我发现了我正在生成的 DynamicMethod 中出现“操作可能会破坏运行时稳定性”的原因,虽然我很容易修复了它,但它给我留下了一个看似简单的问题:
- 如何强制转换类型为“Object”的对象引用转换为特定类型,以便我可以在对象引用上调用该类型的方法?
下面是一个示例程序。运行此方法时,它会在编译该方法时崩溃并出现“操作可能会破坏运行时的稳定性”异常。
只需更改声明为 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();
}
}
}
I discovered the reason I was getting "Operation could destabilize the runtime" in a DynamicMethod I'm producing, and though I easily fixed it, it left me with a seemingly simple question:
- How do I cast an object reference of type "Object" into a specific type, so that I can call methods from that type on the object reference?
Below is a sample program. When running this, it will crash with an "Operation could destabilize the runtime" exception when compiling the method.
The problem is fixed by just changing the type of the variable being declared to be of type TestClass
instead of Object
, but I still want to know how I can cast the reference to the appropriate type in the code.
In the code I've marked a line with asterixes. What can I emit of code at that point that will make the Object
reference on the stack into a TestClass
reference instead, so that the method call will go through?
Note that I know that it is the method call that produces the problem, if I comment out the lines altogether, it doesn't matter which type the variable is, the method is compiled and executes fine.
Here's the code.
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();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
简短的回答:
但是如何做到这一点呢?嗯,我使用的方法是 Reflector。首先,写一个方法来完成你想做的事情。我提出了以下建议:
现在,编译该文件,然后打开 Reflector,然后打开程序集。导航到您的函数并查看它的 MSIL。上面的函数反编译为以下内容:
上面使用
callvirt
而不是call
。我并不是很精通 IL,所以我不确定其中的区别,但call
在您的示例中确实有效。最后一件事是我们讨论 Reflector 的话题。您可以使用 ReflectionEmitLanguage 插件来生成您的为您发出
代码。该插件为您生成以下代码:The short answer:
How to come by that though? Well, the method I used was Reflector. Firstly, write up a method that does what you want to do. I came up with the following:
Now, compile that, and open Reflector, and open your assembly. Navigate to your function and look at the MSIL for it. The function above decompiles to the following:
The above uses
callvirt
instead ofcall
. I'm not really all that proficient in IL, so I'm not sure of the difference, butcall
does work in your example. One last thing, while we're on the topic of Reflector. You can use the ReflectionEmitLanguage addin to great effect to generate yourEmit
code for you. This addin generates the following code for you: