Ян Ньюсон совершенно прав, но если вам нужен код, то вы идете:)
Используя эти два класса, вы можете объединить два предиката. Я не придумал это, но немного улучшил / скорректировал его и заставил использовать тип Predicate
вместо Func
вместе с некоторыми более новыми языковыми функциями (оригинал был довольно старым, к сожалению, я не помню, где янашел).
internal class SubstExpressionVisitor : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> _subst = new Dictionary<Expression, Expression>();
protected override Expression VisitParameter(ParameterExpression node)
{
if (_subst.TryGetValue(node, out Expression newValue))
{
return newValue;
}
return node;
}
public Expression this[Expression original]
{
get => _subst[original];
set => _subst[original] = value;
}
}
public static class PredicateBuilder
{
// you don't seem to need this but it's included for completeness sake
public static Expression<Predicate<T>> And<T>(this Expression<Predicate<T>> a, Expression<Predicate<T>> b)
{
if (a == null)
throw new ArgumentNullException(nameof(a));
if (b == null)
throw new ArgumentNullException(nameof(b));
ParameterExpression p = a.Parameters[0];
SubstExpressionVisitor visitor = new SubstExpressionVisitor();
visitor[b.Parameters[0]] = p;
Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
return Expression.Lambda<Predicate<T>>(body, p);
}
public static Expression<Predicate<T>> Or<T>(this Expression<Predicate<T>> a, Expression<Predicate<T>> b)
{
if (a == null)
throw new ArgumentNullException(nameof(a));
if (b == null)
throw new ArgumentNullException(nameof(b));
ParameterExpression p = a.Parameters[0];
SubstExpressionVisitor visitor = new SubstExpressionVisitor();
visitor[b.Parameters[0]] = p;
Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
return Expression.Lambda<Predicate<T>>(body, p);
}
}
Вы можете использовать это так:
Expression<Predicate<Item>> func1 = (x1) => x1.Color == "Black";
Expression<Predicate<Item>> func2 = (x2) => x2.Categories.Any(y => categories.Select(z => z == y).Any());
Expression<Predicate<Item>> finalExpr = func1.Or(func2);
Возможно, вы хотите иметь в виду, что мой Or
использует OrElse
для внутреннего использованиятаким образом, второе выражение не будет оцениваться, если предыдущее значение равно true (OrElse
похоже на ||
, а не |
). То же самое относится к And
с AndAlso
(AndAlso
похоже на &&
, а не &
).
Еще одна вещь, которую следует отметить, это то, что вы можете легко заменить Predicate<T>
на Func<T, bool>
, если у вас естьиспользовать Func
по некоторым причинам:)