2009-10-13 4 views
4

я обнаружил причину я получаю «Операция может дестабилизировать выполнения» в DynamicMethod я производящий, и хотя я легко исправить, он оставил у меня, казалось бы, простым вопросом:Кастинг объекта для определенного класса в IL?

  • Как бросить ссылку на объект типа «Объект» на определенный тип, чтобы я мог вызывать методы из этого типа в ссылке на объект?

Ниже приведен пример программы. При запуске это приведет к сбою при исключении «Операция может дестабилизировать время выполнения» при компиляции метода.

Проблема заключается в изменении типа объявляемой переменной типа 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(); 
     } 
    } 
} 

ответ

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 действительно работает в вашем примере. Последнее, пока мы на тему Reflector. Вы можете использовать дополнение ReflectionEmitLanguage для создания вашего Emit кода для вас. Этот addin генерирует для вас следующий код:

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. Спасибо за попытку. –

-2

Вы пробовали

(variable as TestClass) 
+0

Я должен исправить код, чтобы сделать это, это мой вопрос, как выглядит этот код. «variable» здесь - объект LocalBuilder, используемый для определения переменной для скомпилированного кода. –

Смежные вопросы