2014-10-21 2 views
1

Я пытаюсь сравнить различные методы вызова конструктора типа, который неизвестен во время компиляции. У меня есть четыре метода работы: прямой вызов конструктора (для сравнения времени), вызов ConstructorInfo.Invoke, вызов Expression.Lambda.Compile и вызов Activator.Create. Тот, с которым я не могу работать, использует DynamicMethod. Вот небольшой пример моего кода:Создание DynamicMethod для вызова конструктора

public struct Foo { 
    private int _val; 
    public Foo(int val) { 
     _val = val; 
    } 
    public override string ToString() { 
     return _val.ToString(); 
    } 
} 

static void Main(string[] args) { 
    var ctorInfo = typeof(Foo).GetConstructor(new[] { typeof(int) }); 
    var ctorDynamic = new DynamicMethod("CreateFoo", 
      typeof (Foo), new[] {typeof (int)}); 

    var ilGenerator = ctorDynamic.GetILGenerator(); 
    // Load the int input parameter onto the stack 
    ilGenerator.Emit(OpCodes.Ldloc_0); 
    // Call the constructor 
    ilGenerator.Emit(OpCodes.Call, ctorInfo); 
    // Return the result of calling the constructor 
    ilGenerator.Emit(OpCodes.Ret); 

    // Create a delegate for calling the constructor 
    var ctorInvoker = (Func<int, Foo>)ctorDynamic.CreateDelegate(
         typeof(Func<int, Foo>)); 

    // Call the constructor 
    var foo = ctorInvoker(5); 
} 

При попытке вызвать конструктор делегата на последней строке, я получаю VerificationException, который говорит «Операция может дестабилизировать выполнения.» Я думаю, что мне не хватает одного или нескольких опкодов, но не знаю, какой из них. Кто-нибудь знает, как правильно это сделать, используя DynamicMethod?

ответ

3

При попытке написать ИЛ наилучшим вариантом является, как правило, написать эквивалентный код C#, а затем посмотреть на сгенерированный ИЛ.

Итак, вы пишете метод, как:

static Foo f(int i) 
{ 
    return new Foo(i); 
} 

И IL будет выглядеть следующим образом:

ldarg.0 
newobj  Foo..ctor 
ret 

Это показывает две ошибки, которые Вы сделали:

  1. загрузить аргумент метода, использование ldarg, ldloc предназначено только для локальных переменных.
  2. Для вызова конструктора необходимо использовать newobj, конструктор не ведет себя как метод Foo -returning.

Если вы исправите эти ошибки, ваш код будет работать.

+0

Из любопытства, как вы посмотрели MSIL? Я вижу, что есть инструмент под названием ildasm, который поставляется с VS2013, но не смог найти файлы * .re, над которыми он должен работать. –

+0

Хотя newobj будет работать, для типов значений это ненормально, поэтому вы не увидели бы вышеперечисленное, выпущенное компилятором. В документах: «Типы значений обычно не создаются с помощью newobj. Обычно они выделяются либо как аргументы, либо локальные переменные, используя newarr (для нулевых, одномерных массивов) или как поля объектов. После выделения они инициализируется с помощью Initobj. Однако инструкция newobj может использоваться для создания нового экземпляра типа значения в стеке, который затем может быть передан в качестве аргумента, сохранен в локальном и т. д. ». –

+0

@ user960115 Да, ildasm - один из вариантов. И он работает на нормальных .Net .DLL и .EXE. – svick