Je crée une classe Validator<T>
. J'essaie d'implémenter les méthodes d'extension Linq SelectMany
pour que mon validateur puisse composer des expressions à l'aide d'une requête Linq et valider le résultat final même lorsque les valeurs sous-jacentes changent.Comment composer des expressions Linq? ie Func <Exp <Func<X, Y>>, Exp <Func<Y, Z>>, Exp <Func<X, Z> >>
Le code de test suivant illustre mon intention.
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);
Je l'ai vu la question suivante How do I compose existing Linq Expressions qui me montre comment composer deux « s ensemble Func<T, bool>
en utilisant une expression And
, mais je dois être en mesure de composer des fonctions ensemble dans un plus, eh bien, de manière fonctionnelle.
J'ai, par exemple, les deux expressions suivantes:
public Expression<Func<T>> ValueExpression { get; private set; }
public Expression<Func<T, bool>> ValidationExpression { get; private set; }
Je souhaite créer une nouvelle expression comme ceci:
public Expression<Func<bool>> IsValidExpression
{
get
{
// TODO: Compose expressions rather than compile & invoke.
}
}
Plus brièvement, je suis en train de créer ces fonctions:
// 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>>>
La fonction générale de cas peut être modifiée pour accepter différents nombres d'arguments génériques ts au besoin pour composer n'importe quelle fonction.
J'ai cherché Stack Overflow (bien sûr) et le web, mais je n'ai pas un exemple qui résout ce problème.
Mon code pour la classe Validator<T>
est ci-dessous.
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.
}
}
}
Mes SelectMany
extensions contiennent des charges de dégueu .Compile().Invoke()
que je veux me débarrasser de.
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);
}
Merci d'avance!
Vraiment du mal à voir ce que vous entendez par "d'une manière plus fonctionnelle". Que devez-vous faire que vous ne pouvez pas faire en supprimant simplement tous les Expression <> .Compile() et .Invoke() s? – pdr
Je suis curieux de savoir pourquoi vous voudriez valider une expression * pour produire la valeur * par opposition à simplement valider une * valeur réelle *. Pouvez-vous élaborer sur ce point? – Aaronaught
Voici un exemple - J'essaie de composer des fonctions telles que f (x) = x + 1 & g (x) = sqrt (x) puis h (x) = f (g (x)). Maintenant, si j'ai une contrainte sur g telle que x> = 0 (sqrt de -ve nombres etc.) alors je veux que cette contrainte se propage à la fonction h. Quand ma valeur sous-jacente de x change je veux pouvoir demander la fonction h si je devrais considérer son résultat comme étant toujours valide. (Ceci est un exemple un peu artificiel, mais cela devrait aider à clarifier.) Bravo. – Enigmativity