C#如何使用ExpressionParameter提供的ConstructorInfo调用Expression.New实例化类
我想在C#的Expression.Block内部通过LINQ的Expression.New实例化一个带有公共非无参构造函数的非泛型类ClassToBeInstantiated。
下方是基于@sweeper的答复更新的内容
问题描述
根据官方文档,支持传入构造参数的Expression.New重载均要求传入ConstructorInfo参数,但我无法提前获取该信息,只能在Expression.Block内部动态获取。我可以通过Expression.Call在传入块的ClassToBeInstantiated类型上获取对应的构造函数信息。
但所有需要传入构造参数的Expression.New重载都仅接受ConstructorInfo作为构造函数标识参数,仅调用无参构造函数时才支持直接传入Type参数。我也无法直接将存储了ConstructorInfo的ParameterExpression作为参数传入Expression.New。
核心疑问
请问有没有办法可以绕过这个限制直接使用Expression.New实现需求?我知道可以通过Expression.Call调用ConstructorInfo.Invoke完成实例化,但在我看来Expression.New API本来就应该支持该场景,请问我是不是遗漏了什么用法?
待实例化类示例
public class ClassToBeInstantiated { public ClassToBeInstantiated(int count) { } }
获取ConstructorInfo的帮助类
public static class GetConstructor { public static ConstructorInfo GetNonGenericConstructorInfo(Type type, params Type[] typeArguments) => type.GetConstructor(typeArguments); }
报错的测试代码示例
[Test] public void InstantiateTypeViaExpressionNew() { var typeExpression = Expression.Parameter(typeof(Type), "type"); var countExpression = Expression.Parameter(typeof(int), "count"); var ctorExpression = Expression.Variable(typeof(ConstructorInfo), "constructorInfo"); var block = Expression.Block ( new[] { ctorExpression }, Expression.Assign(ctorExpression, Expression.Call(typeof(GetConstructor), nameof(GetConstructor.GetNonGenericConstructorInfo), Type.EmptyTypes, typeExpression, Expression.Constant(new[] { countExpression.Type }))), Expression.New(/* 此处报错,无法直接传入ctorExpression作为参数 */, countExpression) ); var lambda = Expression.Lambda<Func<Type, int, object>>(block, typeExpression, countExpression); var func = lambda.Compile(); var o = func.Invoke(typeof(ClassToBeInstantiated), 4); var instance = o as ClassToBeInstantiated; Assert.NotNull(instance); }
使用Expression.Call替代Expression.New的更新方案
基于@Sweeper的答复,我更新了代码示例,供有需要的开发者参考:
更新后的帮助类
我新增了constructorInfoInvokeMethodInfo字段,用于获取ConstructorInfo.Invoke()方法的MethodInfo,供Expression.Block内部调用:
public static class GetConstructor { public static ConstructorInfo GetNonGenericConstructorInfo(Type type, params Type[] typeArguments) => type.GetConstructor(typeArguments); public static readonly MethodInfo constructorInfoInvokeMethodInfo = typeof(ConstructorInfo).GetMethod(nameof(ConstructorInfo.Invoke), new[] { typeof(object[]) }); }
更新后的Expression.Block测试代码
我将无法运行的Expression.New替换为Expression.Call,通过ConstructorInfo.Invoke()完成类型实例化:
[Test] public void InstantiateTypeViaExpressionCall() { var typeExpression = Expression.Parameter(typeof(Type), "type"); var countExpression = Expression.Parameter(typeof(int), "count"); var ctorExpression = Expression.Variable(typeof(ConstructorInfo), "constructorInfo"); var block = Expression.Block ( new[] { ctorExpression }, Expression.Assign ( ctorExpression, Expression.Call(typeof(Activator), nameof(GetNonGenericConstructorInfo), Type.EmptyTypes, typeExpression, Expression.Constant(new[] { countExpression.Type })) ), Expression.Call(ctorExpression, constructorInfoInvokeMethodInfo, Expression.NewArrayInit(typeof(object), Expression.Convert(countExpression, typeof(object)))) ); var lambda = Expression.Lambda<Func<Type, int, object>>(block, typeExpression, countExpression); var func = lambda.Compile(); var o = func.Invoke(typeof(ClassToBeInstantiated), 4); var instance = o as ClassToBeInstantiated; Assert.NotNull(instance); }
内容的提问来源于stack exchange,提问作者Ronald Rink 'd-fens'

