Comment convertir une Chaîne à son équivalent Expression LINQ Arbre?
C'est une version simplifiée du problème original.
J'ai une classe de la Personne appelée:
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 un exemple:
var bob = new Person {
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = '1/1/2000'
}
Je tiens à écrire ce qui suit comme un chaîne dans mon éditeur de texte préféré....
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3
Je voudrais profiter de cette chaîne et mon instance d'objet et d'évaluer une valeur VRAI ou FAUX - c'est à dire l'évaluation d'un Func<Personne, bool> sur l'instance de l'objet.
Ici sont au courant de mes pensées:
- De mettre en œuvre une grammaire de base en ANTLR de soutien de base de Comparaison et d'Opérateurs Logiques. Je pense à de la copie de Visual Basic priorité et certains de la featureset ici: http://msdn.microsoft.com/en-us/library/fw84t893(SV.80).aspx
- Ont ANTLR créer l'AST à partir d'une chaîne de caractères.
- À pied de l'AST et de l'utilisation de la Prédicat Builder cadre pour créer dynamiquement la touche Func<Personne, bool>
- Évaluer le prédicat par rapport à une instance de la Personne, comme l'exige
Ma question est ai-je totalement overbaked cela? des alternatives?
EDIT: Solution Choisie
J'ai décidé d'utiliser la Dynamique de Linq Bibliothèque, plus précisément la Dynamique de la classe de Requête prévu dans le 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();
}
}
}
Résultat est de type System.Boolean, et dans ce cas, est VRAI.
Un grand merci à Marc Gravel.
Inclure Système.Linq.Dynamique package nuget, la documentation ici
- Merci pour l'affichage de la solution complète de code avec votre question. Très apprécié.
- que faire si vous avez une collection ou d'un peuple et à filtrer certains éléments? Personne.Âge > 3 ET par Personne.Poids > 50 ?
- Merci. Je ne peux pas trouver DynamicExpression.ParseLambda(). D'espace de noms et d'assemblage est-il?
- Tout bon.. Il y a une ambiguïté entre les espaces de noms. Nécessaire - à l'aide de E = Système.Linq.Expressions, en utilisant le Système.Linq.Dynamique;
- Pourquoi faut-il utiliser " ET "au lieu de" &&". N'est-il pas censé être le code C#?
- Il pourrait être plus performant à lancer le délégué à la touche Func<Personne,bool> et l'utilisation Invoquer au lieu de DynamicInvoke. Voir ici: stackoverflow.com/questions/12858340/...
- Afin d'obtenir que cela fonctionne, vous devez télécharger C# Requête Dynamique de la Bibliothèque (inclus dans le \LinqSamples\DynamicQuery répertoire), accédez à
CSharpSamples\LinqSamples\DynamicQuery\DynamicQuery
, et ensuite, soit 1) copier ledynamic.cs
classe à partir de l'exemple de projet dans votre projet ou 2) mettredynamic.cs
dans son propre projet de dll séparée avec le nom de fichier et l'espace de noms par défaut deSystem.Linq.Dynamic
(qui est ce que j'ai fait parce que c'est plus SÈCHE. - Requête dynamique de la Bibliothèque est disponible en package NuGet appelé "le Système de.Linq.Dynamique"
Vous devez vous connecter pour publier un commentaire.
Serait le dynamique linq bibliothèque de l'aide ici? En particulier, je suis en train de penser comme un
Where
clause. Si nécessaire, le mettre à l'intérieur d'une liste/tableau d'appeler.Where(string)
sur elle! c'est à direSi pas, d'écrire un analyseur syntaxique (à l'aide de
Expression
sous le capot) n'est pas très imposition, j'ai écrit un semblable (bien que je ne pense pas que j'ai la source) dans mon train commute juste avant noël...// 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!Un autre exemple de la bibliothèque est Fuir
J'ai fait une rapide comparaison de Dynamique Linq Bibliothèque et Fuir et Fuir était 10 fois plus rapide pour l'expression
"(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
Ce la façon dont vous pouvez écrire votre code à l'aide de Fuir.
LinqPad a la
Dump()
méthodevar type = typeof(T); var prop = type.GetProperty(propName);
pour la compiler.Vous pouvez prendre un coup d'oeil à la DLR. Il vous permet d'évaluer et d'exécuter des scripts à l'intérieur .NET 2.0 de l'application. Voici un exemple avec IronRuby:
Bien entendu, cette technique est basée sur l'exécution de l'évaluation et de code ne peut pas être vérifiée au moment de la compilation.
Voici un exemple d'un Scala DSL d'analyseur de combinateur de l'analyse et de l'évaluation des expressions arithmétiques.
L'équivalent de l'expression de l'arbre ou l'arbre d'analyse de l'expression arithmétique serait de l'Analyseur[Liste[String]] type.
Plus de détails sur le lien suivant:
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
En plus Dynamique Linq Bibliothèque (qui s'appuie fortement typé expression et nécessite des variables fortement typées) je recommande une meilleure alternative: linq analyseur partie de NReco Communes Bibliothèque (open source). Il s'adapte à tous les types et effectue toutes les invocations lors de l'exécution et se comporte comme langage dynamique: