gpt4 book ai didi

c# - 如何编写 Linq 表达式?即 Func>, Exp>, Exp>>

转载 作者:可可西里 更新时间:2023-11-01 07:50:35 28 4
gpt4 key购买 nike

我正在创建一个 Validator<T>类(class)。我正在尝试实现 Linq SelectMany我的验证器的扩展方法能够使用 Linq 查询组合表达式并验证最终结果,即使基础值发生变化也是如此。

下面的测试代码展示了我的意图。

var a = 2;
var b = 3;

var va = Validator.Create(() => a, n => n >= 0 && n < 5);
var vb = Validator.Create(() => 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);

我看到了以下问题 How do I compose existing Linq Expressions这告诉我如何组成两个 Func<T, bool>一起使用 And表达式,但我需要能够以更有效的方式将函数组合在一起。

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

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

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

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

更简洁地说,我正在尝试创建这些函数:

// Specific case
Func<Expression<Func<T>>, Expression<Func<T, bool>>, Expression<Func<bool>>>
// General case
Func<Expression<Func<X, Y>>, Expression<Func<Y, Z>>, Expression<Func<X, Z>>>

可以修改一般情况函数以根据需要接受不同数量的通用参数来组成任何函数。

我搜索过 Stack Overflow(当然)和网络,但没有解决此问题的示例。

我的 Validator<T> 代码类在下面。

public class Validator<T>
{
public Validator(Expression<Func<T>> valueFunc,
Expression<Func<T, bool>> validationFunc)
{
this.ValueExpression = valueFunc;
this.ValidationExpression = validationFunc;
}

public Expression<Func<T>> ValueExpression { get; private set; }
public Expression<Func<T, bool>> 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<bool>> IsValidExpression
{
get
{
// TODO: Compose expressions.
}
}
}

我的 SelectMany扩展包含大量令人讨厌的 .Compile().Invoke()我想摆脱它。

public static Validator<U> SelectMany<T, U>(this Validator<T> @this, Expression<Func<T, Validator<U>>> k)
{
Expression<Func<T>> fvtv = @this.ValueExpression;
Expression<Func<Validator<U>>> fvu = () => k.Compile().Invoke(fvtv.Compile().Invoke());
Expression<Func<U>> fvuv = fvu.Compile().Invoke().ValueExpression;
Expression<Func<U, bool>> fvtiv = u => @this.ValidationExpression.Compile().Invoke(fvtv.Compile().Invoke());
return fvuv.ToValidator(fvtiv);
}

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

提前致谢!

最佳答案

同时 dtb's答案适用于多种情况,它不是最优的,因为这样的表达式不能在 Entity Framework 中使用,因为它无法处理 Invoke 调用。不幸的是,要避免这些调用,需要更多代码,包括新的 ExpressionVisitor 派生类:

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);
}
}

这会将第一个表达式中第一个参数的每个实例替换为第二个表达式中的表达式。所以这样的调用:

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

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

关于c# - 如何编写 Linq 表达式?即 Func<Exp<Func<X, Y>>, Exp<Func<Y, Z>>, Exp<Func<X, Z>>>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2330843/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com