使用表达式创建生成器失败:运行时动态类型实例生成器问题
解决表达式树构建报错与动态实例生成器实现方案
首先来说你碰到的InvalidOperationException问题——核心原因是你在Expression.Block里引用了objCastedAsEln和objHub这两个局部变量,但没把它们加入到Block的局部变量列表中。你现在只添加了inputParam,但它是Lambda的入参,不属于Block的局部变量范畴,所以表达式树找不到这两个变量的定义,自然就报错了。
修正后的表达式树实现
我们可以简化表达式树的结构(其实不需要多余的赋值步骤),同时把需要的局部变量加入Block的变量列表,修正后的代码如下:
public Func<object, object> GetHubObjectGenerator(Type elnObjectType, Type hubObjectType) { // 定义Lambda的输入参数 ParameterExpression inputParam = Expression.Parameter(typeof(object), "input"); // 将输入对象转换为目标eln类型 Expression castToEln = Expression.TypeAs(inputParam, elnObjectType); // 获取Hub类型对应的构造函数(参数为eln类型) ConstructorInfo constructor = hubObjectType.GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { elnObjectType }, null); if (constructor == null) { throw new ArgumentException($"未找到{hubObjectType.Name}类型接收{elnObjectType.Name}参数的构造函数"); } // 调用构造函数创建Hub实例 Expression createHubInstance = Expression.New(constructor, castToEln); // 将Hub实例转换为object类型返回 Expression castToObject = Expression.TypeAs(createHubInstance, typeof(object)); // 构建并编译Lambda表达式 var lambda = Expression.Lambda<Func<object, object>>(castToObject, inputParam); return lambda.Compile(); }
代码说明:
- 去掉了不必要的局部变量声明和赋值操作,直接通过链式调用完成类型转换、实例构造、返回转换,让表达式树结构更简洁
- 增加了构造函数不存在的异常判断,避免后续出现空引用错误
- 最终生成的委托逻辑和你期望的示例完全一致,只是类型是运行时动态确定的:
input => new HubGroup(input as elnGroup)
泛型版本的适配方案
如果你想保留泛型版本的实现,同时支持运行时动态调用,可以结合反射来触发泛型方法。比如在你的ApiDataHandler中可以这样改造:
// 提前缓存泛型方法的MethodInfo,避免重复反射 private static readonly MethodInfo _genericGeneratorMethod = typeof(ApiDataHandler) .GetMethod(nameof(GetHubObjectGeneratorGeneric), BindingFlags.NonPublic | BindingFlags.Instance); // 泛型版本的生成器方法 private Func<TEln, THub> GetHubObjectGeneratorGeneric<TEln, THub>() where TEln : elnObject where THub : HubObject { ParameterExpression argParam = Expression.Parameter(typeof(TEln), "objEln"); ConstructorInfo constructor = typeof(THub).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(TEln) }, null); if (constructor == null) { throw new ArgumentException($"未找到{typeof(THub).Name}类型接收{typeof(TEln).Name}参数的构造函数"); } return Expression.Lambda<Func<TEln, THub>>( Expression.New(constructor, argParam), argParam).Compile(); } // 在SoFunny方法中调用泛型版本 public void SoFunny<T>(string strContent, T objHub) where T : HubObject { if (typeof(T) == typeof(HubGroup)) { if (string.IsNullOrEmpty(strContent)) { throw new Exception("Cannot parse null/empty string! ---ApiDataHandler---"); } var objEln = JsonConvert.DeserializeObject<elnGroup>(strContent); // 通过反射动态构造泛型方法并调用 var genericMethod = _genericGeneratorMethod.MakeGenericMethod(objEln.GetType(), typeof(T)); var generator = (Func<object, object>)Delegate.CreateDelegate( typeof(Func<object, object>), this, genericMethod); var result = generator(objEln); // 这里可以对生成的Hub实例做后续处理 } }
为什么需要反射调用泛型版本?
因为在SoFunny方法中,你只有T(Hub类型)的编译时信息,而eln类型是通过反序列化动态得到的,无法直接在编译时确定泛型参数,所以必须通过反射动态构造泛型方法实例,再转换为Func<object, object>来使用。
测试验证
当你调用修正后的GetHubObjectGenerator(typeof(elnGroup), typeof(HubGroup))时,生成的委托逻辑完全等价于你期望的示例代码,不会再出现变量未定义的错误。
内容的提问来源于stack exchange,提问作者SunainaDG
相关产品推荐
相关产品推荐

