【发布时间】:2010-02-25 00:55:09
【问题描述】:
我正在创建一个Validator<T> 类。我正在尝试为我的验证器实现 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,它向我展示了如何使用And 表达式将两个Func<T, bool> 组合在一起,但我需要能够以更有效的方式组合函数方式。
例如,我有以下两个表达式:
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>>>
通用 case 函数可以根据需要修改为接受不同数量的通用参数来组成任何函数。
我已经搜索过 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);
}
提前致谢!
【问题讨论】:
-
真的很难理解“以更实用的方式”是什么意思。仅删除所有 Expression .Compile() 和 .Invoke() 无法完成的操作需要做什么?
-
我很好奇你为什么要验证一个表达式来产生值,而不是简单地验证一个实际值。你能详细说明这一点吗?
-
这是一个例子 - 我正在尝试组合函数,例如 f(x) = x + 1 & g(x) = sqrt(x) 然后 h(x) = f(g(x ))。现在,如果我对 g 有一个约束,使得 x >= 0(-ve 数的平方等),那么我希望该约束传播到函数 h。当我的 x 基础值发生变化时,我希望能够询问函数 h 我是否应该认为它的结果仍然有效。 (这是一个有些人为的例子,但它应该有助于澄清。)干杯。
-
我认为您可能会对这个堆栈交换 proposal 感兴趣。如果它是显示您的支持并帮助它进入测试版。
标签: c# .net linq linq-expressions