Skip to content

Instantly share code, notes, and snippets.

@Eonasdan
Created April 13, 2020 20:21
Show Gist options
  • Select an option

  • Save Eonasdan/16c0a12a8c972633f9b33c3c3d13c825 to your computer and use it in GitHub Desktop.

Select an option

Save Eonasdan/16c0a12a8c972633f9b33c3c3d13c825 to your computer and use it in GitHub Desktop.
Predicate Extensions
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
namespace Extensions
{
[DebuggerStepThrough]
public static class PredicateExtensions
{
/// <summary>
/// Begin an expression chain
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">Default return value if the chain is ended early</param>
/// <returns>A lambda expression stub</returns>
public static Expression<Func<T, bool>> Begin<T>(bool value = false)
{
if (value) return parameter => true; //value cannot be used in place of true/false
return parameter => false;
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left,
Expression<Func<T, bool>> right)
{
return CombineLambdas(left, right, ExpressionType.AndAlso);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
return CombineLambdas(left, right, ExpressionType.OrElse);
}
#region private
//HEY IT SAYS PRIVATE! j/k
private static Expression<Func<T, bool>> CombineLambdas<T>(this Expression<Func<T, bool>> left,
Expression<Func<T, bool>> right, ExpressionType expressionType)
{
//Remove expressions created with Begin<T>()
if (IsExpressionBodyConstant(left)) return right;
var p = left.Parameters[0];
var visitor = new SubstituteParameterVisitor { Substitute = { [right.Parameters[0]] = p } };
Expression body = Expression.MakeBinary(expressionType, left.Body, visitor.Visit(right.Body));
return Expression.Lambda<Func<T, bool>>(body, p);
}
private static bool IsExpressionBodyConstant<T>(Expression<Func<T, bool>> left)
{
return left.Body.NodeType == ExpressionType.Constant;
}
internal class SubstituteParameterVisitor : ExpressionVisitor
{
public readonly Dictionary<Expression, Expression> Substitute = new Dictionary<Expression, Expression>();
protected override Expression VisitParameter(ParameterExpression node)
{
return Substitute.TryGetValue(node, out var newValue) ? newValue : node;
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment