有人可以向我解释为什么以下内容适用于第一个测试但是会为第二个测试抛出InvalidProgramException吗?我很难过。
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace DMTest
{
class Program
{
static void Main(string[] args)
{
Test.NewResultWithParam();
Console.WriteLine("Press any key to continue");
Console.ReadLine();
Test.NewResult();
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
}
public static class Test
{
public static void NewResult()
{
var f = typeof(Result).New<Result>();
var result = f.Invoke();
}
public static void NewResultWithParam()
{
var f = typeof(Result).New<Param, Result>();
var result = f.Invoke(new Param("Parameter Executed"));
result.Build();
}
}
public static class DynamicFunctions
{
public static Func<R> New<R>(this Type type)
{
if (!typeof(R).IsAssignableFrom(type))
{
throw new ArgumentException();
}
var dm = BuildNewMethod(type, Type.EmptyTypes);
return (Func<R>)dm.CreateDelegate(typeof(Func<R>));
}
public static Func<T, R> New<T, R>(this Type type)
{
if (!typeof(R).IsAssignableFrom(type))
{
throw new ArgumentException();
}
var dm = BuildNewMethod(type, new Type[] { typeof(T) });
return (Func<T, R>)dm.CreateDelegate(typeof(Func<T, R>));
}
private static DynamicMethod BuildNewMethod(Type newObjType, Type[] parameterTypes)
{
var dm = new DynamicMethod("New_" + newObjType.FullName, newObjType, parameterTypes, newObjType);
var info = newObjType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, parameterTypes, new ParameterModifier[0]);
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Newobj, info);
il.Emit(OpCodes.Ret);
return dm;
}
}
public class Result
{
private Param _param;
private Result()
{
Console.WriteLine("Parameterless constructor called");
}
private Result(Param param)
{
Console.WriteLine("Parametered constructor called");
_param = param;
}
public void Build()
{
_param.Execute();
}
}
public class Param
{
private string _s;
public Param(string s)
{
_s = s;
}
public void Execute()
{
Console.WriteLine(_s);
}
}
}
答案 0 :(得分:3)
il.Emit(OpCodes.Ldarg_0); // <-- problem here
由于您的动态方法在New<T>
版本中没有参数,因此您无法使用Ldarg_0
。此外,Newobj
将期望零参数,因此您的堆栈是不平衡的。