如何撰写Linq表达式? 即Func <Exp <Func >,Exp <Func >,Exp <Func >>

我正在创建一个Validator类。 我正在尝试为我的validation器实现Linq SelectMany扩展方法,以便能够使用Linq查询组合表达式,并在基础值发生更改时validation最终结果。

以下测试代码演示了我的意图。

 var a = 2; var b = 3; var va = Validator.Create(() => a, n => n >= 0 && n  b, n => n >= 0 && n < 5); var vc = from ia in va from ib in vb select ia + ib; Debug.Assert(vc.Value == a + b); //2 + 3 Debug.Assert(vc.Value == 5); Debug.Assert(vc.IsValid == true); a = 7; Debug.Assert(vc.Value == a + b); //7 + 3 Debug.Assert(vc.Value == 10); Debug.Assert(va.IsValid == false); Debug.Assert(vb.IsValid == true); Debug.Assert(vc.IsValid == false); 

我已经看到了以下问题我如何编写现有的Linq表达式 ,它向我展示了如何使用And表达式组合两个Func ,但是我需要能够在更多,更好地组合函数,function方式。

例如,我有以下两个表达式:

 public Expression<Func> ValueExpression { get; private set; } public Expression<Func> ValidationExpression { get; private set; } 

我想创建一个像这样的新表达式:

  public Expression<Func> IsValidExpression { get { // TODO: Compose expressions rather than compile & invoke. } } 

更简洁地我正在尝试创建这些function:

 // Specific case Func<Expression<Func>, Expression<Func>, Expression<Func>> // General case Func<Expression<Func>, Expression<Func>, Expression<Func>> 

可以修改一般案例函数以根据需要接受不同数量的generics参数来组成任何函数。

我搜索过Stack Overflow(当然)和网络,但没有一个例子可以解决这个问题。

我的Validator类代码如下。

 public class Validator { public Validator(Expression<Func> valueFunc, Expression<Func> validationFunc) { this.ValueExpression = valueFunc; this.ValidationExpression = validationFunc; } public Expression<Func> ValueExpression { get; private set; } public Expression<Func> ValidationExpression { get; private set; } public T Value { get { return this.ValueExpression.Compile().Invoke(); } } public bool IsValid { get { return this.IsValidExpression.Compile().Invoke(); } } public Expression<Func> IsValidExpression { get { // TODO: Compose expressions. } } } 

我的SelectMany扩展包含大量.Compile().Invoke()我想摆脱的.Compile().Invoke()

 public static Validator SelectMany(this Validator @this, Expression<Func<T, Validator>> k) { Expression<Func> fvtv = @this.ValueExpression; Expression<Func<Validator>> fvu = () => k.Compile().Invoke(fvtv.Compile().Invoke()); Expression<Func> fvuv = fvu.Compile().Invoke().ValueExpression; Expression<Func> fvtiv = u => @this.ValidationExpression.Compile().Invoke(fvtv.Compile().Invoke()); return fvuv.ToValidator(fvtiv); } public static Validator SelectMany(this Validator @this, Expression<Func<T, Validator>> k, Expression<Func> s) { Expression<Func<Validator>> fvu = () => @this.SelectMany(k); Expression<Func> fvtv = @this.ValueExpression; Expression<Func> fvuv = fvu.Compile().Invoke().ValueExpression; Expression<Func> fvtiv = @this.ValidationExpression; Expression<Func> fvuiv = u => fvu.Compile().Invoke().ValidationExpression.Compile().Invoke(u); Expression<Func> fvv = () => s.Compile().Invoke(fvtv.Compile().Invoke(), fvuv.Compile().Invoke()); Expression<Func> fvviv = v => fvtiv.Compile().Invoke(fvtv.Compile().Invoke()) && fvuiv.Compile().Invoke(fvuv.Compile().Invoke()); return fvv.ToValidator(fvviv); } 

提前致谢!

相当于Haskell的函数组合运算符

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

在C#中可能是类似的东西

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

这是你在找什么?

例:

 Compose(y => y.ToString(), x => x + 1).Compile()(10); // "11" 

虽然dtb的答案适用于多种场景,但它不是最理想的,因为它无法在Entity Framework中使用,因为它无法处理Invoke调用。 不幸的是,为了避免这些调用,需要更多的代码,包括一个新的ExpressionVisitor派生类:

 static Expression> Compose(Expression> f, Expression> g) { var ex = ReplaceExpressions(f.Body, f.Parameters[0], g.Body); return Expression.Lambda>(ex, g.Parameters[0]); } static TExpr ReplaceExpressions(TExpr expression, Expression orig, Expression replacement) where TExpr : Expression { var replacer = new ExpressionReplacer(orig, replacement); return replacer.VisitAndConvert(expression, "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) { if (node == From) { return To; } return base.Visit(node); } } 

这将使用第二个表达式中的表达式替换第一个表达式中第一个参数的每个实例。 所以像这样的电话:

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

将产生表达式(Class2 c2) => c2.Class1Property.StringProperty