이것은 원래 문제의 단순화 된 버전입니다.
Person이라는 클래스가 있습니다.
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
… 인스턴스를 말하십시오.
var bob = new Person {
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = '1/1/2000'
}
좋아하는 텍스트 편집기에서 다음을 문자열 로 작성하고 싶습니다 ….
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3
이 문자열과 객체 인스턴스를 가져 와서 TRUE 또는 FALSE를 평가하고 싶습니다. 즉, 객체 인스턴스에서 Func <Person, bool>을 평가합니다.
내 현재 생각은 다음과 같습니다.
- 기본 비교 및 논리 연산자를 지원하기 위해 ANTLR에서 기본 문법을 구현하십시오. Visual Basic 우선 순위와 일부 기능 집합을 여기에 복사하려고합니다. http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
- ANTLR이 제공된 문자열에서 적절한 AST를 작성하도록하십시오.
- AST를 걷고 Predicate Builder 프레임 워크를 사용하여 Func <Person, bool>을 동적으로 작성하십시오.
- 필요에 따라 Person 인스턴스에 대한 술어 평가
내 질문은 내가 완전히 오버 베이크 한 것입니까? 대안이 있습니까?
편집 : 선택한 솔루션
동적 Linq 라이브러리, 특히 LINQSamples에 제공된 동적 쿼리 클래스를 사용하기로 결정했습니다.
아래 코드 :
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();
}
}
}
결과는 System.Boolean 유형이며이 경우 TRUE입니다.
Marc Gravell에게 감사드립니다.
System.Linq.Dynamic 너겟 패키지, 문서를 여기에 포함 하십시오
답변
겠습니까 라이브러리 LINQ 동적 여기에 도움을? 특히, 나는 Where
절로 생각하고 있습니다. 필요한 경우 목록 / 배열 안에 넣으면 .Where(string)
됩니다. 즉
var people = new List<Person> { person };
int match = people.Where(filter).Any();
그렇지 않다면, 파서 ( Expression
후드에서 사용)를 작성하는 것은 큰 과세가 아닙니다. 크리스마스 바로 전에 기차 통근에 비슷한 것을 썼습니다 (내가 소스를 가지고 있다고 생각하지는 않지만).
답변
또 다른 도서관은 도망입니다
Dynamic Linq Library 와 Flee and Flee 의 빠른 비교 를 통해 표현 속도가 10 배 빨랐습니다."(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
Flee를 사용하여 코드를 작성할 수있는 방법입니다.
static void Main(string[] args)
{
var context = new ExpressionContext();
const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3";
context.Variables.DefineVariable("Person", typeof(Person));
var e = context.CompileDynamic(exp);
var bob = new Person
{
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = new DateTime(2000, 1, 1)
};
context.Variables["Person"] = bob;
var result = e.Evaluate();
Console.WriteLine(result);
Console.ReadKey();
}
답변
void Main()
{
var testdata = new List<Ownr> {
//new Ownr{Name = "abc", Qty = 20}, // uncomment this to see it getting filtered out
new Ownr{Name = "abc", Qty = 2},
new Ownr{Name = "abcd", Qty = 11},
new Ownr{Name = "xyz", Qty = 40},
new Ownr{Name = "ok", Qty = 5},
};
Expression<Func<Ownr, bool>> func = Extentions.strToFunc<Ownr>("Qty", "<=", "10");
func = Extentions.strToFunc<Ownr>("Name", "==", "abc", func);
var result = testdata.Where(func.ExpressionToFunc()).ToList();
result.Dump();
}
public class Ownr
{
public string Name { get; set; }
public int Qty { get; set; }
}
public static class Extentions
{
public static Expression<Func<T, bool>> strToFunc<T>(string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
{
Expression<Func<T, bool>> func = null;
try
{
var type = typeof(T);
var prop = type.GetProperty(propName);
ParameterExpression tpe = Expression.Parameter(typeof(T));
Expression left = Expression.Property(tpe, prop);
Expression right = Expression.Convert(ToExprConstant(prop, value), prop.PropertyType);
Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(ApplyFilter(opr, left, right), tpe);
if (expr != null)
innerExpr = innerExpr.And(expr);
func = innerExpr;
}
catch (Exception ex)
{
ex.Dump();
}
return func;
}
private static Expression ToExprConstant(PropertyInfo prop, string value)
{
object val = null;
try
{
switch (prop.Name)
{
case "System.Guid":
val = Guid.NewGuid();
break;
default:
{
val = Convert.ChangeType(value, prop.PropertyType);
break;
}
}
}
catch (Exception ex)
{
ex.Dump();
}
return Expression.Constant(val);
}
private static BinaryExpression ApplyFilter(string opr, Expression left, Expression right)
{
BinaryExpression InnerLambda = null;
switch (opr)
{
case "==":
case "=":
InnerLambda = Expression.Equal(left, right);
break;
case "<":
InnerLambda = Expression.LessThan(left, right);
break;
case ">":
InnerLambda = Expression.GreaterThan(left, right);
break;
case ">=":
InnerLambda = Expression.GreaterThanOrEqual(left, right);
break;
case "<=":
InnerLambda = Expression.LessThanOrEqual(left, right);
break;
case "!=":
InnerLambda = Expression.NotEqual(left, right);
break;
case "&&":
InnerLambda = Expression.And(left, right);
break;
case "||":
InnerLambda = Expression.Or(left, right);
break;
}
return InnerLambda;
}
public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Func<T, TResult> ExpressionToFunc<T, TResult>(this Expression<Func<T, TResult>> expr)
{
var res = expr.Compile();
return res;
}
}
LinqPad 는 Dump()
방법이 있습니다
답변
DLR을 살펴볼 수 있습니다 . .NET 2.0 응용 프로그램 내에서 스크립트를 평가하고 실행할 수 있습니다. IronRuby를 사용한 샘플은 다음과 같습니다 .
using System;
using IronRuby;
using IronRuby.Runtime;
using Microsoft.Scripting.Hosting;
class App
{
static void Main()
{
var setup = new ScriptRuntimeSetup();
setup.LanguageSetups.Add(
new LanguageSetup(
typeof(RubyContext).AssemblyQualifiedName,
"IronRuby",
new[] { "IronRuby" },
new[] { ".rb" }
)
);
var runtime = new ScriptRuntime(setup);
var engine = runtime.GetEngine("IronRuby");
var ec = Ruby.GetExecutionContext(runtime);
ec.DefineGlobalVariable("bob", new Person
{
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = "1/1/2000"
});
var eval = engine.Execute<bool>(
"return ($bob.Age > 3 && $bob.Weight > 50) || $bob.Age < 3"
);
Console.WriteLine(eval);
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public string FavouriteDay { get; set; }
}
물론이 기법은 런타임 평가를 기반으로하며 컴파일 타임에 코드를 확인할 수 없습니다.
답변
다음은 산술 표현식의 구문 분석 및 평가를위한 Scala DSL 기반 파서 결합기의 예입니다.
import scala.util.parsing.combinator._
/**
* @author Nicolae Caralicea
* @version 1.0, 04/01/2013
*/
class Arithm extends JavaTokenParsers {
def expr: Parser[List[String]] = term ~ rep(addTerm | minusTerm) ^^
{ case termValue ~ repValue => termValue ::: repValue.flatten }
def addTerm: Parser[List[String]] = "+" ~ term ^^
{ case "+" ~ termValue => termValue ::: List("+") }
def minusTerm: Parser[List[String]] = "-" ~ term ^^
{ case "-" ~ termValue => termValue ::: List("-") }
def term: Parser[List[String]] = factor ~ rep(multiplyFactor | divideFactor) ^^
{
case factorValue1 ~ repfactor => factorValue1 ::: repfactor.flatten
}
def multiplyFactor: Parser[List[String]] = "*" ~ factor ^^
{ case "*" ~ factorValue => factorValue ::: List("*") }
def divideFactor: Parser[List[String]] = "/" ~ factor ^^
{ case "/" ~ factorValue => factorValue ::: List("/") }
def factor: Parser[List[String]] = floatingPointConstant | parantExpr
def floatingPointConstant: Parser[List[String]] = floatingPointNumber ^^
{
case value => List[String](value)
}
def parantExpr: Parser[List[String]] = "(" ~ expr ~ ")" ^^
{
case "(" ~ exprValue ~ ")" => exprValue
}
def evaluateExpr(expression: String): Double = {
val parseRes = parseAll(expr, expression)
if (parseRes.successful) evaluatePostfix(parseRes.get)
else throw new RuntimeException(parseRes.toString())
}
private def evaluatePostfix(postfixExpressionList: List[String]): Double = {
import scala.collection.immutable.Stack
def multiply(a: Double, b: Double) = a * b
def divide(a: Double, b: Double) = a / b
def add(a: Double, b: Double) = a + b
def subtract(a: Double, b: Double) = a - b
def executeOpOnStack(stack: Stack[Any], operation: (Double, Double) => Double): (Stack[Any], Double) = {
val el1 = stack.top
val updatedStack1 = stack.pop
val el2 = updatedStack1.top
val updatedStack2 = updatedStack1.pop
val value = operation(el2.toString.toDouble, el1.toString.toDouble)
(updatedStack2.push(operation(el2.toString.toDouble, el1.toString.toDouble)), value)
}
val initial: (Stack[Any], Double) = (Stack(), null.asInstanceOf[Double])
val res = postfixExpressionList.foldLeft(initial)((computed, item) =>
item match {
case "*" => executeOpOnStack(computed._1, multiply)
case "/" => executeOpOnStack(computed._1, divide)
case "+" => executeOpOnStack(computed._1, add)
case "-" => executeOpOnStack(computed._1, subtract)
case other => (computed._1.push(other), computed._2)
})
res._2
}
}
object TestArithmDSL {
def main(args: Array[String]): Unit = {
val arithm = new Arithm
val actual = arithm.evaluateExpr("(12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) * ( 5 + 12 ))")
val expected: Double = (12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) * ( 5 + 12 ))
assert(actual == expected)
}
}
제공된 산술 표현식의 동등한 표현식 트리 또는 구문 분석 트리는 Parser [List [String]] 유형입니다.
자세한 내용은 다음 링크를 참조하십시오.
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
답변
Dynamic Linq Library (강력한 형식의 표현식을 작성하고 강력한 형식의 변수가 필요함) 외에도 NReco Commons Library의 일부인 linq 파서 (오픈 소스)를 사용 하는 것이 좋습니다 . 모든 유형을 정렬하고 런타임에 모든 호출을 수행하며 동적 언어처럼 작동합니다.
var lambdaParser = new NReco.LambdaParser();
var varContext = new Dictionary<string,object>();
varContext["one"] = 1M;
varContext["two"] = "2";
Console.WriteLine( lambdaParser.Eval("two>one && 0<one ? (1+8)/3+1*two : 0", varContext) ); // --> 5
답변
이것은 상대적으로 오래된 게시물이지만 표현식 작성기의 코드입니다. AnyService-ExpressionTreeBuilder
다음은 단위 테스트입니다. AnyService-ExpressionTreeBuilder 단위 테스트