Что не так с IL, который я излучаю в Reflection.Emit.DynamicMethod? - PullRequest
3 голосов
/ 12 марта 2012

В идеале я хочу использовать делегат CreateObject, потому что код, генерирующий эти динамические методы, предназначен для десериализатора, который должен иметь возможность обрабатывать любой тип (по крайней мере, примитивы, структуры и экземпляры классов).Однако я столкнулся с проблемой с типом делегата CreateObject, поэтому решил попробовать делегат CreateRectangle для отладки.Я немного приблизился к рабочему решению, но что-то еще не так.Что не так с моим кодом для обоих случаев?То есть, как я могу заставить динамический метод работать для CreateObject и CreateRectangle?Или мой код вызова является виновником?

Вывод:

{X=0,Y=0,Width=0,Height=0}

Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

Exception has been thrown by the target of an invocation.
    Common Language Runtime detected an invalid program.

Код:

using System;
using System.Drawing;
using System.Reflection.Emit;

namespace Experiments {
    public class Program {
        private delegate object CreateObject();
        private delegate Rectangle CreateRectangle();

        static void Main() {
            Console.WriteLine(new Rectangle());
            Console.WriteLine();
            var dm = BuildDynamicMethod();
            TryCreateDelegate<CreateObject>(dm);
            Console.WriteLine();
            TryCreateDelegate<CreateRectangle>(dm);
            Console.WriteLine();
            Console.ReadKey();
        }

        private static void TryCreateDelegate<T>(DynamicMethod dm) {
            try {
                var co = dm.CreateDelegate(typeof (T));
                var value = co.DynamicInvoke(null);
                Console.WriteLine(value);
            } catch (Exception ex) {
                Console.WriteLine(ex.Message);
                var indent = 0;
                while (ex.InnerException != null) {
                    indent++;
                    ex = ex.InnerException;
                    Console.WriteLine(new string('\t', indent) + ex.Message);
                }
            }
        }

        private static DynamicMethod BuildDynamicMethod() {
            var tr = typeof(Rectangle);
            var dm = new DynamicMethod("buildNewRectangle", tr, Type.EmptyTypes);
            var il = dm.GetILGenerator();
            il.Emit(OpCodes.Ldloca_S, (byte)0);
            il.Emit(OpCodes.Initobj, tr);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Box, tr);
            il.Emit(OpCodes.Ret);
            return dm;
        }
    }
}

Ответы [ 2 ]

3 голосов
/ 12 марта 2012
private delegate object CreateObject();
private delegate Rectangle CreateRectangle();

static void Main()
{
  Console.WriteLine(new Rectangle());
  Console.WriteLine();
  TryCreateDelegate<CreateObject>(BuildDynamicMethod_Boxed());
  Console.WriteLine();
  TryCreateDelegate<CreateRectangle>(BuildDynamicMethod());
  Console.WriteLine();
}

private static DynamicMethod BuildDynamicMethod_Boxed()
{
  var TRect = typeof(Rectangle);
  var dm = new DynamicMethod("buildNewRectangle", typeof(object), Type.EmptyTypes);
  var il = dm.GetILGenerator();
  il.Emit(OpCodes.Ldloca_S, il.DeclareLocal(TRect));
  il.Emit(OpCodes.Initobj, TRect);
  il.Emit(OpCodes.Ldloc_0);      
  il.Emit(OpCodes.Box, TRect);
  il.Emit(OpCodes.Ret);
  return dm;
}

private static DynamicMethod BuildDynamicMethod()
{
  var TRect = typeof(Rectangle);
  var dm = new DynamicMethod("buildNewRectangle", TRect, Type.EmptyTypes);
  var il = dm.GetILGenerator();
  il.Emit(OpCodes.Ldloca_S, il.DeclareLocal(TRect));
  il.Emit(OpCodes.Initobj, TRect);
  il.Emit(OpCodes.Ldloc_0);
  il.Emit(OpCodes.Ret);
  return dm;
}

private static void TryCreateDelegate<T>(DynamicMethod dm)
{
  try
  {
    var co = dm.CreateDelegate(typeof(T));
    var value = co.DynamicInvoke();
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
    var indent = 0;
    while (ex.InnerException != null)
    {
      indent++;
      ex = ex.InnerException;
      Console.WriteLine(new string('\t', indent) + ex.Message);
    }
  }
}
3 голосов
/ 12 марта 2012

Я нашел самый простой способ понять, что является действительным ILCode, - это начать с написания кода на C #, скомпилировать его и затем попробовать прочитать декомпилированный ILCode.Это может быть очень информативно.

Но похоже, что вы пытаетесь ссылаться на локальные переменные без объявления адресного пространства ни для каких.LdLoca_S ссылается на локальную переменную, и прежде чем вы сможете это сделать, вам нужно вызвать .DeclareLocal (tr).Не уверен, что это единственная проблема, но это самая очевидная проблема.

Редактировать: ОК, я запустил ее сам, и она действительно работает, когда вы добавляете туда .DeclareLocal (tr), но былатакже проблема с сигнатурой метода.

Но вы пытаетесь вызвать его с двумя разными сигнатурами метода.Если вы возвращаете объект, вам нужно его упаковать, но если вы вернете Rectangle, вы не сможете его упаковать.Но ваш возвращаемый тип жестко запрограммирован на typeof (Rectangle).Итак, вы пытаетесь пометить прямоугольник и затем вернуть прямоугольник в виде структуры прямоугольника.Либо не ставьте коробку, либо измените тип возврата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...