我有两种类型: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.
}
Run Code Online (Sandbox Code Playgroud)
要么已经有了这样或应该有的东西.
luk*_*san 14
这是使用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;
}
Run Code Online (Sandbox Code Playgroud)
更新:自从我第一次回答这个问题已经有1.5年了,我想我现在会扩展我的答案,因为当你有一个表达而不是委托时,人们会问如何做到这一点.
该解决方案在原理上是一样的-我们需要能够组成两个函数(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;
}
Run Code Online (Sandbox Code Playgroud)
因此,创建我们的组合函数的唯一方法是使用类自己构建表达式树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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我创建了一个扩展方法,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]);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,有了所有基础设施,我们可以修改我们的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;
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个简单的控制台应用程序来测试它.希望我的解释不会太混淆; 但不幸的是,没有一种更简单的方法可以做你想做的事情.如果你仍然完全困惑,至少你可以重用我的代码,并对表达树处理的细微差别和复杂性有所了解!
| 归档时间: |
|
| 查看次数: |
4779 次 |
| 最近记录: |