我在Reflection.Emit.DynamicMethod中发出的IL有什么问题?

时间:2012-03-12 01:45:47

标签: c# .net reflection

理想情况下,我希望使用CreateObject委托,因为生成这些动态方法的代码适用于应该能够处理任何类型(至少是基元,结构和类实例)的反序列化器。但是,我遇到CreateObject委托类型的问题所以我决定尝试CreateRectangle委托来调试事情。我更接近一个有效的解决方案,但其他东西是不对的。我的代码对于这两种情况有什么问题?也就是说,如何使动态方法适用于CreateObjectCreateRectangle?或者,我的调用代码是罪魁祸首吗?

输出:

{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 个答案:

答案 0 :(得分:3)

我发现理解什么是有效ILCode最简单的方法是先编写C#代码,编译它,然后尝试阅读反编译的ILCode。它可以提供非常丰富的信息。

但看起来你试图引用局部变量而不为任何声明地址空间。 LdLoca_S引用了一个局部变量,在你能够做到之前,你需要调用.DeclareLocal(tr)。不确定这是否是唯一的问题,但这是最明显的问题。

编辑:好的,我自己运行它,一旦你在那里添加.DeclareLocal(tr)它确实有效,但方法签名也存在问题。

但是你试图用两种不同的方法签名来调用它。如果返回对象,则需要将其打包,但如果返回Rectangle,则无法将其打包。但是你的返回类型是硬编码为typeof(Rectangle)。因此,您尝试将Rectangle包装起来,然后将盒装矩形作为矩形结构返回。要么不装箱,要么改变你的退货类型。

答案 1 :(得分:3)

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