Dies ist eine vereinfachte Version des ursprünglichen Problems.
Ich habe eine Klasse namens Person:
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
... und sagen wir eine Instanz:
var bob = new Person {
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = '1/1/2000'
}
Ich möchte Folgendes als Zeichenfolge in meinen bevorzugten Texteditor schreiben ...
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3
Ich möchte diesen String und meine Objektinstanz nehmen und TRUE oder FALSE auswerten, dh eine Func <Person, bool> auf der Objektinstanz auswerten.
Hier sind meine aktuellen Gedanken:
- Implementieren Sie eine grundlegende Grammatik in ANTLR, um grundlegende Vergleichs- und logische Operatoren zu unterstützen. Ich denke darüber nach, die Visual Basic-Priorität und einige der hier aufgeführten Funktionen zu kopieren: http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
- Lassen Sie ANTLR aus einer bereitgestellten Zeichenfolge einen geeigneten AST erstellen.
- Gehen Sie durch den AST und verwenden Sie das Predicate Builder- Framework, um die Func <Person, bool> dynamisch zu erstellen
- Bewerten Sie das Prädikat nach Bedarf anhand einer Instanz von Person
Meine Frage ist, habe ich das total überbacken? irgendwelche Alternativen?
BEARBEITEN: Gewählte Lösung
Ich habe mich für die Dynamic Linq Library entschieden, insbesondere für die in den LINQSamples bereitgestellte Dynamic Query-Klasse.
Code unten:
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();
}
}
}
Das Ergebnis ist vom Typ System.Boolean und in diesem Fall TRUE.
Vielen Dank an Marc Gravell.
Fügen Sie System.Linq.Dynamic nuget Paket, Dokumentation hier
Antworten:
Würde die dynamische Linq-Bibliothek hier helfen? Insbesondere denke ich als
Where
Klausel. Wenn nötig, legen Sie es in eine Liste / ein Array, um es aufzurufen.Where(string)
! dhWenn nicht, ist das Schreiben eines Parsers (
Expression
unter der Haube) nicht sehr anstrengend - ich habe kurz vor Weihnachten einen ähnlichen Parser ( obwohl ich nicht glaube, dass ich die Quelle habe) in meinem Zugverkehr geschrieben ...quelle
// 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 gut!Eine andere solche Bibliothek ist Flee
Ich habe einen schnellen Vergleich von Dynamic Linq Library und Flee durchgeführt und Flee war zehnmal schneller für den Ausdruck
"(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
So können Sie Ihren Code mit Flee schreiben.
quelle
LinqPad hat die
Dump()
Methodequelle
var type = typeof(T); var prop = type.GetProperty(propName);
, um ihn kompilieren zu können.Sie können sich das DLR ansehen . Sie können damit Skripts in der .NET 2.0-Anwendung auswerten und ausführen. Hier ist ein Beispiel mit IronRuby :
Natürlich basiert diese Technik auf der Laufzeitauswertung und der Code kann zur Kompilierungszeit nicht überprüft werden.
quelle
Hier ist ein Beispiel eines Scala DSL-basierten Parser-Kombinators zum Parsen und Auswerten von arithmetischen Ausdrücken.
Der äquivalente Ausdrucksbaum oder Analysebaum des angegebenen arithmetischen Ausdrucks wäre vom Typ Parser [List [String]].
Weitere Details finden Sie unter folgendem Link:
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
quelle
Zusätzlich zur Dynamic Linq Library (die stark typisierte Ausdrücke erstellt und stark typisierte Variablen erfordert) empfehle ich eine bessere Alternative: Linq-Parser für diesen Teil der NReco Commons Library (Open Source). Es richtet alle Typen aus und führt alle Aufrufe zur Laufzeit aus und verhält sich wie eine dynamische Sprache:
quelle
Obwohl dies ein relativ alter Beitrag ist, ist dies der Code für den Ausdrucksgenerator: AnyService - ExpressionTreeBuilder Dies sind die Komponententests : AnyService - ExpressionTreeBuilder-Komponententests
quelle