2009-10-13 76 views
4

我發現我越來越「操作可能會破壞運行時」在DynamicMethod的我產生的原因,雖然我很容易固定它,它給我留下了一個看似簡單的問題:將對象投射到IL中的特定類中?

  • 我怎麼投一個類型爲「Object」的對象引用爲特定的類型,這樣我可以在該對象引用上調用該類型的方法?

下面是一個示例程序。運行時,編譯該方法時會發生「操作可能會使運行時不穩定」異常。

該問題是通過更改被聲明爲TestClass而不是Object類型的變量的類型來解決的,但我仍想知道如何將引用轉換爲代碼中的相應類型。

在代碼中,我用asterixes標記了一行。我可以在那個時候發出什麼代碼,以便將堆棧中的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(); 
     } 
    } 
} 

回答

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; 
} 

現在,編譯和開放反射器,並打開您的裝配。導航到你的功能,並查看它的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在你的例子中起作用。最後一件事,當我們談論反射器的話題時。您可以使用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; 
} 
+0

對不起,忽略你的答案這麼久,我的代碼中有一個錯誤,使得它看起來像Castclass不是正確的選擇,但是當我發現這一點時,我立即忘記了我的SO問題。感謝您的努力。 – 2009-10-30 17:33:01

-2

你試過

(variable as TestClass) 
+0

我必須發出代碼來做到這一點,那是我的問題,代碼是什麼樣的。這裏的「變量」是一個LocalBuilder對象,用於爲編譯後的代碼定義變量。 – 2009-10-13 08:30:34