运行时类型未知时,如何编译Expression<Func<ObjType,TDest>>?
解决运行时动态类型Expression的编译问题
问题场景
你定义了如下方法:
public static TDest SimpleStuff<TDest>(object obj) where TDest : new() { var objType = obj.GetType(); var destinationType = typeof(TDest); var method = typeof(SimpleMapper).GetMethod("CreateInit", BindingFlags.Static | BindingFlags.Public, Type.EmptyTypes); var genericMethod = method.MakeGenericMethod(objType, destinationType); var expressionFuncAsObject = genericMethod.Invoke(typeof(SimpleMapper), null); // ... 后续代码 }
其中expressionFuncAsObject的实际类型为Expression<Func<ObjType, TDest>>,但由于ObjType和TDest的具体类型仅在运行时可知,无法直接强转为该类型以调用.Compile()方法。
你尝试用Expression.GetFuncType配合Expression.Lambda编译,代码如下:
var funcType = Expression.GetFuncType(objType , destinationType); var func = Expression.Lambda(funcType, Expression.Parameter(destinationType), Expression.Parameter(objType ) ).Compile();
但运行时抛出错误:
System.InvalidOperationException: 'variable '' of type 'B' referenced from scope '', but it is not defined'
解决方案
核心原因
你手动构造的ParameterExpression和原表达式expressionFuncAsObject中引用的参数并非同一实例,表达式树验证时会判定引用了未定义的变量,因此报错。正确的做法是直接利用已有表达式对象,通过反射调用其Compile方法。
实现代码
- 调用已有表达式的Compile方法
// 获取expressionFuncAsObject实例的Compile方法 var compileMethod = expressionFuncAsObject.GetType().GetMethod("Compile", Type.EmptyTypes); // 执行Compile得到委托实例 var compiledFunc = compileMethod.Invoke(expressionFuncAsObject, null);
- 执行编译后的委托
如果需要执行该委托并返回结果,可通过DynamicInvoke方法(所有委托都继承自Delegate类,自带该方法):
// 传入obj作为参数,执行委托 var result = compiledFunc.GetType().GetMethod("DynamicInvoke").Invoke(compiledFunc, new[] { obj }); // 强转为TDest类型返回 return (TDest)result;
补充说明
不需要重新构造Lambda表达式,因为原表达式expressionFuncAsObject已经是一个完整的Expression<Func<...>>实例,包含了正确的参数定义和逻辑,直接调用它自己的Compile方法即可完成编译。
内容的提问来源于stack exchange,提问作者Nachman Berkowitz
相关产品推荐
相关产品推荐

