To jest uproszczona wersja pierwotnego problemu.
Mam klasę o nazwie Osoba:
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
... i powiedzmy przykład:
var bob = new Person {
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = '1/1/2000'
}
Chciałbym napisać następujący tekst jako ciąg w moim ulubionym edytorze tekstu ....
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3
Chciałbym wziąć ten ciąg i moją instancję obiektu i ocenić TRUE lub FALSE - tj. Oszacować Func <Person, bool> na instancji obiektu.
Oto moje aktualne przemyślenia:
- Zaimplementuj podstawową gramatykę w ANTLR, aby obsługiwać podstawowe porównania i operatory logiczne. Myślę o skopiowaniu priorytetu Visual Basic i niektórych funkcji tutaj: http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
- Poproś ANTLR o utworzenie odpowiedniego AST z dostarczonego ciągu.
- Przejdź przez AST i użyj Predicate Builder , aby dynamicznie utworzyć obiekt Func <Person, bool>
- Oceń predykat względem instancji Person zgodnie z wymaganiami
Moje pytanie brzmi: czy całkowicie przesadziłem? jakieś alternatywy?
EDYCJA: wybrane rozwiązanie
Zdecydowałem się użyć Dynamic Linq Library, a konkretnie klasy Dynamic Query dostarczonej w LINQSamples.
Kod poniżej:
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();
}
}
}
Wynik jest typu System.Boolean iw tym przypadku ma wartość TRUE.
Wielkie dzięki dla Marc Gravell.
Dołącz pakiet nuget System.Linq.Dynamic , dokumentacja tutaj
Odpowiedzi:
Czy dynamiczna biblioteka linq pomogłaby tutaj? W szczególności myślę jako
Where
klauzula. Jeśli to konieczne, umieść go w liście / tablicy tylko po to, aby go wywołać.Where(string)
! to znaczyJeśli nie, pisanie parsera (
Expression
pod maską) nie jest zbytnio obciążające - jeden podobny (choć chyba nie mam źródła) napisałem w pociągu dojeżdżającym tuż przed świętami ...źródło
// 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 dobrze!Inną taką biblioteką jest Uciekaj
Zrobiłem szybkie porównanie Dynamic Linq Library i Flee and Flee było 10 razy szybsze dla wyrażenia
"(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
W ten sposób możesz napisać kod za pomocą Flee.
źródło
LinqPad ma
Dump()
metodęźródło
var type = typeof(T); var prop = type.GetProperty(propName);
go skompilować.Możesz rzucić okiem na DLR . Pozwala na ocenę i wykonywanie skryptów wewnątrz aplikacji .NET 2.0. Oto próbka z IronRuby :
Oczywiście technika ta opiera się na ocenie środowiska uruchomieniowego i kodu nie można zweryfikować w czasie kompilacji.
źródło
Oto przykład kombinatora parsera Scala opartego na DSL do analizowania i oceny wyrażeń arytmetycznych.
Równoważne drzewo wyrażeń lub drzewo parsowania podanego wyrażenia arytmetycznego byłoby typu Parser [List [String]].
Więcej szczegółów znajduje się pod poniższym linkiem:
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
źródło
Oprócz Dynamic Linq Library (która buduje silnie wpisane wyrażenia i wymaga silnie wpisanych zmiennych) polecam lepszą alternatywę: parser linq będący częścią NReco Commons Library (open source). Wyrównuje wszystkie typy i wykonuje wszystkie wywołania w czasie wykonywania i zachowuje się jak język dynamiczny:
źródło
Chociaż jest to stosunkowo stary post - oto kod dla konstruktora wyrażeń: AnyService - ExpressionTreeBuilder Oto testy jednostkowe: AnyService - ExpressionTreeBuilder Testy jednostkowe
źródło