You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在运行时创建Func<未知类型, bool>并替换表达式树中的类型?

解决方案:运行时构建泛型表达式与委托

你遇到的核心问题是编译时无法确定泛型类型,需要通过反射和表达式树在运行时动态构建逻辑。下面分步骤拆解实现思路和代码:

1. 核心思路梳理

要实现ForeignKeyProperty.SetValue(model, repository.GetList<ForeignKeyProperty.PropertyType>().Single(x => x.Id == model.Id));这个逻辑,我们需要分三步动态处理:

  • 构建针对运行时类型的过滤委托Func<UnknownType, bool>
  • 动态调用泛型方法GetList<UnknownType>()和Single<UnknownType>()
  • 最终完成属性赋值

2. 具体实现代码

假设你已经拿到了ForeignKeyProperty(PropertyInfo实例)、model(ViewModel实例)和repository(IVenturaRepository实例),可以按以下代码实现:

// 1. 获取运行时才知道的实体类型
Type entityType = ForeignKeyProperty.PropertyType;

// 2. 构建过滤表达式:x => x.Id == model.Id
// 创建参数表达式(x)
ParameterExpression paramExpr = Expression.Parameter(entityType, "x");
// 获取实体的Id属性(假设所有实体都有名为Id的属性,可根据实际调整)
PropertyInfo entityIdProp = entityType.GetProperty("Id");
if (entityIdProp == null)
    throw new InvalidOperationException($"类型{entityType.Name}未找到Id属性");

// 构建x.Id的表达式
Expression leftExpr = Expression.Property(paramExpr, entityIdProp);
// 构建model.Id的常量表达式(注意类型匹配)
object modelIdValue = typeof(ViewModel).GetProperty("Id").GetValue(model);
Expression rightExpr = Expression.Constant(modelIdValue, entityIdProp.PropertyType);

// 构建相等表达式
BinaryExpression equalsExpr = Expression.Equal(leftExpr, rightExpr);
// 构建Lambda表达式并编译为委托
LambdaExpression filterLambda = Expression.Lambda(equalsExpr, paramExpr);
Delegate filterDelegate = filterLambda.Compile();

// 3. 动态调用repository.GetList<T>()
MethodInfo getListMethod = typeof(IVenturaRepository)
    .GetMethod("GetList", Type.EmptyTypes)
    .MakeGenericMethod(entityType);
IEnumerable entityList = (IEnumerable)getListMethod.Invoke(repository, null);

// 4. 动态调用Enumerable.Single<T>(source, predicate)
MethodInfo singleMethod = typeof(Enumerable)
    .GetMethods(BindingFlags.Public | BindingFlags.Static)
    .First(m => m.Name == "Single" && m.GetParameters().Length == 2)
    .MakeGenericMethod(entityType);
object matchedEntity = singleMethod.Invoke(null, new object[] { entityList, filterDelegate });

// 5. 给ForeignKeyProperty赋值
ForeignKeyProperty.SetValue(model, matchedEntity);

3. 针对List<Action<IVenturaRepository, ViewModel>>的适配

因为你的Action是针对ViewModel的,但我们需要在内部处理运行时类型,直接使用现有泛型Action是可行的——在Action内部封装上述动态逻辑即可。比如:

// 假设你在某个上下文中已经获取了ForeignKeyProperty
PropertyInfo foreignKeyProp = ...;

// 将动态逻辑封装到Action中
Action<IVenturaRepository, ViewModel> action = (repo, model) =>
{
    // 把上面的核心实现代码放这里
    Type entityType = foreignKeyProp.PropertyType;
    // ... 其余步骤同上 ...
};

// 添加到你的List中
yourActionList.Add(action);

4. 注意事项

  • 属性存在性检查:实际代码中要确保entityType确实包含Id属性,避免空引用异常
  • 类型转换:model.Id的类型要和entityType.Id的类型匹配,如果可能有不一致,需要添加类型转换表达式(比如Expression.Convert)
  • 方法重载选择:获取Single方法时,要确保选对带Predicate参数的重载,避免因为重载匹配错误导致异常

内容的提问来源于stack exchange,提问作者Nico Haegens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:56:50