我有两种类型:Cat
和 Dog
.我想使用 Func<Dog, bool>
选择猫.为此,我需要一种在某种映射器中将 Cat 的属性映射到 Dog 的方法(类似于 AutoMapper 将一个对象的属性映射到另一种类型的对象的方式)。
我正在想象这样的事情:
public Cat GetCat(Func<Dog, bool> selector)
{
Func<Cat, bool> mappedSelector = getMappedSelector(selector);
return _catRepository.Get(mappedSelector);
}
private Func<Cat, bool> getMappedSelector(Func<Dog, bool> selector)
{
//some code here to map from one function type to another
//something like AutoMapper would be sweet...
//something that I can configure how I want the properties to be mapped.
}
要么已经有一些东西可以做到这一点,要么应该有。
最佳答案
这是一个使用 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# 中无法从另一个表达式“调用”一个表达式(就像我们可以使用委托(delegate)一样),因此我们无法直接在代码中表示这一点。例如,下面的代码将无法编译:
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;
}
因此,创建组合函数的唯一方法是构建 expression tree我们自己使用 System.Linq.Expressions
类。
我们真正需要做的是修改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来测试一下。希望我的解释不会太含糊;但不幸的是,并没有真正更简单的方法来做你想做的事情。如果您仍然一头雾水,至少您可以重用我的代码,并了解处理表达式树的细微差别和复杂性!
关于c# - Func 在选择器类型之间的 AutoMapper,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7424501/