Esta é uma versão simplificada do problema original.
Eu tenho uma classe chamada Pessoa:
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public int Weight { get; set; }
public DateTime FavouriteDay { get; set; }
}
... e digamos uma instância:
var bob = new Person {
Name = "Bob",
Age = 30,
Weight = 213,
FavouriteDay = '1/1/2000'
}
Gostaria de escrever o seguinte como uma string no meu editor de texto favorito ....
(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3
Gostaria de pegar essa string e minha instância de objeto e avaliar um VERDADEIRO ou FALSO - ou seja, avaliar um Func <Pessoa, bool> na instância do objeto.
Aqui estão os meus pensamentos atuais:
- Implemente uma gramática básica no ANTLR para oferecer suporte a operadores lógicos e de comparação. Estou pensando em copiar a precedência do Visual Basic e alguns dos recursos aqui: http://msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
- Faça com que o ANTLR crie um AST adequado a partir de uma sequência fornecida.
- Caminhe pelo AST e use o Predicate Builder estrutura para criar dinamicamente o Func <Person, bool>
- Avalie o predicado em relação a uma instância de Person, conforme necessário
Minha pergunta é se eu cozinhei demais isso? alguma alternativa?
EDIT: Solução Escolhida
Decidi usar a biblioteca Dynamic Linq, especificamente a classe Dynamic Query fornecida nos LINQSamples.
Código abaixo:
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();
}
}
}
O resultado é do tipo System.Boolean e, neste caso, é VERDADEIRO.
Muito obrigado a Marc Gravell.
Inclua o pacote de nuget System.Linq.Dynamic , documentação aqui
Respostas:
A biblioteca linq dinâmica ajudaria aqui? Em particular, estou pensando como uma
Where
cláusula. Se necessário, coloque-o em uma lista / matriz apenas para acessá.Where(string)
-lo! ieCaso contrário, escrever um analisador (usando
Expression
sob o capô) não é muito exigente - escrevi um similar (embora não ache que tenha a fonte) no meu trajeto de trem pouco antes do Natal ...fonte
// 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 good!Outra dessas bibliotecas é a Flee
Fiz uma rápida comparação da biblioteca Dynamic Linq e a fuga e fuga foram 10 vezes mais rápidas para a expressão
"(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
É assim que você pode escrever seu código usando Flee.
fonte
LinqPad tem o
Dump()
métodofonte
var type = typeof(T); var prop = type.GetProperty(propName);
para fazê-lo compilar.Você pode dar uma olhada no DLR . Ele permite avaliar e executar scripts dentro do aplicativo .NET 2.0. Aqui está uma amostra com o IronRuby :
Obviamente, essa técnica é baseada na avaliação do tempo de execução e o código não pode ser verificado no tempo de compilação.
fonte
Aqui está um exemplo de um combinador de analisador baseado em DSL da Scala para analisar e avaliar expressões aritméticas.
A árvore de expressão equivalente ou a árvore de análise da expressão aritmética fornecida seriam do tipo Parser [List [String]].
Mais detalhes estão no seguinte link:
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
fonte
Além da Dynamic Linq Library (que cria expressões fortemente tipadas e requer variáveis fortemente tipadas), recomendo uma alternativa melhor: o analisador linq que faz parte da NReco Commons Library (código aberto). Alinha todos os tipos e executa todas as chamadas em tempo de execução e se comporta como linguagem dinâmica:
fonte
Embora essa postagem seja relativamente antiga - este é o código para o construtor de expressões: AnyService - ExpressionTreeBuilder Estes são os testes de unidade: AnyService - ExpressionTreeBuilder Unit Tests
fonte