How do I compose Linq Expressions? ie Func>, Exp>, Exp>>

前端 未结 2 540
[愿得一人]
[愿得一人] 2020-12-08 16:22

I\'m creating a Validator class. I\'m attempting to implement the Linq SelectMany extension methods for my validator to be able to compose

相关标签:
2条回答
  • 2020-12-08 16:48

    The equivalent of Haskell's function composition operator

    (.) :: (b->c) -> (a->b) -> (a->c)
    f . g = \ x -> f (g x)
    

    would in C# probably be something like

    static Expression<Func<A, C>> Compose<A, B, C>(
        Expression<Func<B, C>> f,
        Expression<Func<A, B>> g)
    {
        var x = Expression.Parameter(typeof(A));
        return Expression.Lambda<Func<A, C>>(
            Expression.Invoke(f, Expression.Invoke(g, x)), x);
    }
    

    Is this what you're looking for?

    Example:

    Compose<int, int, string>(y => y.ToString(), x => x + 1).Compile()(10); // "11"
    
    0 讨论(0)
  • 2020-12-08 17:14

    While dtb's answer works for several scenarios, it is suboptimal as such an expression cannot be used in Entity Framework, as it cannot handle Invoke calls. Unfortunately, to avoid those calls one needs a lot more code, including a new ExpressionVisitor derived class:

    static Expression<Func<A, C>> Compose<A, B, C>(Expression<Func<B, C>> f,
                                                   Expression<Func<A, B>> g)
    {
        var ex = ReplaceExpressions(f.Body, f.Parameters[0], g.Body);
    
        return Expression.Lambda<Func<A, C>>(ex, g.Parameters[0]);
    }
    
    static TExpr ReplaceExpressions<TExpr>(TExpr expression,
                                           Expression orig,
                                           Expression replacement)
        where TExpr : Expression 
    {
        var replacer = new ExpressionReplacer(orig, replacement);
    
        return replacer.VisitAndConvert(expression, nameof(ReplaceExpressions));
    }
    
    private class ExpressionReplacer : ExpressionVisitor
    {
        private readonly Expression From;
        private readonly Expression To;
    
        public ExpressionReplacer(Expression from, Expression to)
        {
            From = from;
            To = to;
        }
    
        public override Expression Visit(Expression node)
        {
            return node == From ? To : base.Visit(node);
        }
    }
    

    This replaces every instance of the first parameter in the first expression with the expression in the second expression. So a call like this:

    Compose((Class1 c) => c.StringProperty, (Class2 c2) => c2.Class1Property

    Would yield the expression (Class2 c2) => c2.Class1Property.StringProperty.

    0 讨论(0)
提交回复
热议问题