我想使用Reflection Emit来创建具有任意构造函数参数的类的实例。这就是我的代码的样子:
public delegate object ObjectActivator(params object[] args);
static void Main(string[] args)
{
var ao = new { ID = 10000, FName = "Sample", SName = "Name" };
var t = ao.GetType();
var info = t.GetConstructor(new Type[] { typeof(int), typeof(string), typeof(string) });
var objActivatorEmit = GetActivatorEmit(info);
var obj = createdActivatorEmit(4, "Foo", "Bar");
}
public static ObjectActivator GetActivatorEmit(ConstructorInfo ctor)
{
ParameterInfo[] paramsInfo = ctor.GetParameters();
DynamicMethod method = new DynamicMethod("CreateInstance", typeof(object), new Type[] { typeof(object[]) });
ILGenerator gen = method.GetILGenerator();
for (int i = 0; i < paramsInfo.Length; i++)
{
Type t = paramsInfo[i].ParameterType;
gen.Emit(OpCodes.Ldarg_0); // Push array (method argument)
gen.Emit(OpCodes.Ldc_I4, i); // Push i
gen.Emit(OpCodes.Ldelem_Ref); // Pop array and i and push array[i]
if( t.IsValueType )
{
gen.Emit( OpCodes.Unbox_Any, t ); // Cast to Type t
}
else
{
gen.Emit( OpCodes.Castclass, t ); //Cast to Type t
}
}
gen.Emit(OpCodes.Newobj, ctor);
gen.Emit(OpCodes.Ret);
return (ObjectActivator)method.CreateDelegate(typeof(ObjectActivator));
}
代码以MethodAccessException
失败,错误消息为Attempt by method 'DynamicClass.CreateInstance(System.Object[])' to access method '<>f__AnonymousType1'3<System.Int32,System.__Canon,System.__Canon>..ctor(Int32, System.__Canon, System.__Canon)' failed.
。
出了什么问题?
答案 0 :(得分:3)
错误消息表明匿名类型的构造函数不是公共的。我认为匿名类型构造函数总是internal
,因此您需要使用不同的DynamicMethod
构造函数来跳过可见性检查:
DynamicMethod method = new DynamicMethod("CreateInstance", typeof(object), new Type[] { typeof(object[]) }, true);
请注意,这在部分信任方案中不起作用。
答案 1 :(得分:2)
你不需要使用Reflection.Emit,我建议你不要。除非你知道你正在做什么或者有一些其他API无法满足的特殊需求,否则最好远离它。
有3种选择更容易正确。看看:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class App
{
public delegate object ObjectActivator(params object[] args);
public static void Main(string[] args)
{
var ao = new { ID = 10000, FName = "Sample", SName = "Name" };
var t = ao.GetType();
var info = t.GetConstructor(new[] { typeof(int), typeof(string), typeof(string) });
if (info == null)
{
throw new Exception("Info is null");
}
// This uses System.Linq.Expressions to create the delegate
var activator = GetActivator(info);
var newObj1 = activator(4, "Foo", "Bar");
// This invokes the ConstructorInfo directly
var newObj2 = info.Invoke(new object[] { 4, "Foo", "Bar" });
// This uses System.Activator to dynamically create the instance
var newObj3 = Activator.CreateInstance(t, 4, "Foo", "Bar");
}
// This uses System.Linq.Expressions to generate a delegate
public static ObjectActivator GetActivator(ConstructorInfo ctor)
{
var args = Expression.Parameter(typeof(object[]), "args");
var parameters = ctor.GetParameters().Select((parameter, index) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(index)), parameter.ParameterType));
return Expression.Lambda<ObjectActivator>(Expression.New(ctor, parameters), args).Compile();
}
}
注意:来自this post
的GetActivator
方法的灵感