这是使用 AutoMapper 的解决方案:
Func<Cat, bool> GetMappedSelector(Func<Dog, bool> selector)
{
Func<Cat, Dog> mapper = Mapper.CreateMapExpression<Cat, Dog>().Compile();
Func<Cat, bool> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
更新:自从我第一次回答这个问题已经 1.5 年了,我想我现在应该扩展我的答案,因为当你有一个表达而不是一个表达时,人们会问如何做到这一点委托。
解决方案在原理上是相同的——我们需要能够将compose 两个函数(selector 和mapper)合并为一个函数。不幸的是,由于在 C# 中没有办法从另一个表达式“调用”一个表达式(就像我们可以使用委托一样),我们不能直接在代码中表示它。例如下面的代码会编译失败:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
因此,创建组合函数的唯一方法是使用 System.Linq.Expressions 类自己构建 expression tree。
我们真正需要做的是修改selector 函数的主体,使其参数的所有实例都替换为mapper 函数的主体。这将成为我们新函数的主体,它将接受mapper 的参数。
为了替换参数,我创建了ExpressionVisitor 类的子类,它可以遍历表达式树并将单个参数替换为任意表达式:
class ParameterReplacer : ExpressionVisitor
{
private ParameterExpression _parameter;
private Expression _replacement;
private ParameterReplacer(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
public static Expression Replace(Expression expression, ParameterExpression parameter, Expression replacement)
{
return new ParameterReplacer(parameter, replacement).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression parameter)
{
if (parameter == _parameter)
{
return _replacement;
}
return base.VisitParameter(parameter);
}
}
然后我创建了一个扩展方法Compose(),它使用访问者组成两个 lambda 表达式,一个外部和一个内部:
public static class FunctionCompositionExtensions
{
public static Expression<Func<X, Y>> Compose<X, Y, Z>(this Expression<Func<Z, Y>> outer, Expression<Func<X, Z>> inner)
{
return Expression.Lambda<Func<X ,Y>>(
ParameterReplacer.Replace(outer.Body, outer.Parameters[0], inner.Body),
inner.Parameters[0]);
}
}
现在,有了所有基础设施,我们可以修改 GetMappedSelector() 方法以使用我们的 Compose() 扩展:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = selector.Compose(mapper);
return mappedSelector;
}
我创建了一个simple console application 来测试它。希望我的解释不会太混乱。但不幸的是,没有更简单的方法来做你想做的事情。如果您仍然完全感到困惑,至少您可以重用我的代码,并且对处理表达式树的细微差别和复杂性有所了解!