Il s'agit d'une version simplifiée du problème d'origine.
J'ai une classe appelée Person:
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
... et disons une instance:
var bob = new Person {
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = '1/1/2000'
}
Je voudrais écrire ce qui suit sous forme de chaîne dans mon éditeur de texte préféré ...
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3
Je voudrais prendre cette chaîne et mon instance d'objet et évaluer un TRUE ou FALSE - c'est-à-dire évaluer un Func <Person, bool> sur l'instance d'objet.
Voici mes pensées actuelles:
- Implémentez une grammaire de base dans ANTLR pour prendre en charge la comparaison de base et les opérateurs logiques. Je pense à copier la priorité Visual Basic et certaines fonctionnalités ici: http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
- Demandez à ANTLR de créer un AST approprié à partir d'une chaîne fournie.
- Parcourez l'AST et utilisez le framework Predicate Builder pour créer dynamiquement le Func <Person, bool>
- Évaluez le prédicat par rapport à une instance de Person comme requis
Ma question est: ai-je totalement surchargé cela? des alternatives?
EDIT: Solution choisie
J'ai décidé d'utiliser la bibliothèque Dynamic Linq, en particulier la classe Dynamic Query fournie dans LINQSamples.
Code ci-dessous:
using System;
using System.Linq.Expressions;
using System.Linq.Dynamic;
namespace ExpressionParser
{
class Program
{
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
static void Main()
{
const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3";
var p = Expression.Parameter(typeof(Person), "Person");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, exp);
var bob = new Person
{
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = new DateTime(2000,1,1)
};
var result = e.Compile().DynamicInvoke(bob);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
Le résultat est de type System.Boolean, et dans ce cas est TRUE.
Un grand merci à Marc Gravell.
Inclure le package nuget System.Linq.Dynamic, documentation ici
Réponses:
La bibliothèque linq dynamique serait-elle utile ici? En particulier, je pense comme une
Where
clause. Si nécessaire, placez-le dans une liste / un tableau juste pour l'appeler.Where(string)
! c'est à direSinon, écrire un analyseur (en utilisant
Expression
sous le capot) n'est pas extrêmement éprouvant - j'en ai écrit un similaire (bien que je ne pense pas avoir la source) dans mon trajet en train juste avant Noël ...la source
// Lambda expression as data in the form of an expression tree.
System.Linq.Expressions.Expression<Func<int, bool>> expr = i => i < 5;
// Compile the expression tree into executable code.
Func<int, bool> deleg = expr.Compile();
// Invoke the method and print the output.
Console.WriteLine("deleg(4) = {0}", deleg(4));
ParseLambda bon!Une autre bibliothèque de ce type est Flee
J'ai fait une comparaison rapide de Dynamic Linq Library et Flee and Flee était 10 fois plus rapide pour l'expression
"(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
Voici comment vous pouvez écrire votre code en utilisant Flee.
la source
LinqPad a la
Dump()
méthodela source
var type = typeof(T); var prop = type.GetProperty(propName);
le faire compiler.Vous pouvez jeter un œil au DLR . Il vous permet d'évaluer et d'exécuter des scripts dans l'application .NET 2.0. Voici un exemple avec IronRuby :
Bien sûr, cette technique est basée sur l'évaluation d'exécution et le code ne peut pas être vérifié au moment de la compilation.
la source
Voici un exemple de combinateur d'analyseur basé sur Scala DSL pour l'analyse et l'évaluation d'expressions arithmétiques.
L'arbre d'expression équivalent ou l'arbre d'analyse de l'expression arithmétique fournie serait du type Parser [List [String]].
Plus de détails sont sur le lien suivant:
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
la source
En plus de Dynamic Linq Library (qui construit une expression fortement typée et nécessite des variables fortement typées), je recommande une meilleure alternative: l'analyseur linq qui fait partie de NReco Commons Library (open source). Il aligne tous les types et effectue toutes les invocations au moment de l'exécution et se comporte comme un langage dynamique:
la source
Bien que ce soit un post relativement ancien - c'est le code du générateur d'expression: AnyService - ExpressionTreeBuilder Voici les tests unitaires: AnyService - ExpressionTreeBuilder Tests unitaires
la source