Вот решение с использованием 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 года с тех пор, как я впервые ответил на это, и я решил, что сейчас расширю свой ответ, так как люди спрашивают, как это сделать, когда у вас есть выражение в отличие от делегата .
Решение в принципе то же самое - нам нужно иметь возможность объединить две функции (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
классы.
Что нам действительно нужно сделать, так это изменить тело функции 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()
, который использует посетителя для составления двух лямбда-выражений, внешнего и внутреннего:
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;
}
Я создал простое консольное приложение , чтобы проверить это. Надеюсь, мое объяснение не было слишком запутанным; но, к сожалению, на самом деле не существует более простого подхода к тому, что вы пытаетесь сделать. Если вы все еще в замешательстве, по крайней мере, вы можете повторно использовать мой код и по достоинству оценить нюансы и сложности работы с деревьями выражений!