É possível compilar e executar novos códigos em tempo de execução no .NET?

Nota: A avaliação da expressão matemática não é o foco desta questão. Eu quero compilar e executar novo código em tempo de execução no .net. Dito isto …

Gostaria de permitir que o usuário insira qualquer equação, como a seguinte, em uma checkbox de texto:

x = x / 2 * 0.07914 x = x^2 / 5 

E ter essa equação aplicada aos pontos de dados de input. Os pontos de dados de input são representados por x e cada ponto de dados é processado pela equação especificada pelo usuário. Eu fiz isso anos atrás, mas não gostei da solução porque exigia analisar o texto da equação para cada cálculo:

 float ApplyEquation (string equation, float dataPoint) { // parse the equation string and figure out how to do the math // lots of messy code here... } 

Quando você está processando cargas de dados de pontos, isso apresenta um pouco de sobrecarga. Eu gostaria de poder traduzir a equação em uma function, em tempo real, de modo que ela só tenha que ser analisada uma vez. Seria algo parecido com isto:

 FunctionPointer foo = ConvertEquationToCode(equation); .... x = foo(x); // I could then apply the equation to my incoming data like this 

A function ConvertEquationToCode iria analisar a equação e retornar um ponteiro para uma function que aplica a matemática apropriada.

O aplicativo basicamente estaria escrevendo um novo código em tempo de execução. Isso é possível com o .NET?

Sim! Usando methods encontrados nos espaços de nomes Microsoft.CSharp , System.CodeDom.Compiler e System.Reflection . Aqui está um aplicativo de console simples que compila uma class (“SomeClass”) com um método (“Add42”) e, em seguida, permite que você chame esse método. Este é um exemplo básico que eu gravei para evitar que as barras de rolagem apareçam na exibição do código. É apenas para demonstrar a compilation e usar o novo código em tempo de execução.

 using Microsoft.CSharp; using System; using System.CodeDom.Compiler; using System.Reflection; namespace RuntimeCompilationTest { class Program { static void Main(string[] args) { string sourceCode = @" public class SomeClass { public int Add42 (int parameter) { return parameter += 42; } }"; var compParms = new CompilerParameters{ GenerateExecutable = false, GenerateInMemory = true }; var csProvider = new CSharpCodeProvider(); CompilerResults compilerResults = csProvider.CompileAssemblyFromSource(compParms, sourceCode); object typeInstance = compilerResults.CompiledAssembly.CreateInstance("SomeClass"); MethodInfo mi = typeInstance.GetType().GetMethod("Add42"); int methodOutput = (int)mi.Invoke(typeInstance, new object[] { 1 }); Console.WriteLine(methodOutput); Console.ReadLine(); } } } 

Você pode tentar isto: Calculator.Net

Ele irá avaliar uma expressão matemática.

A partir da postagem, ele suportará o seguinte:

 MathEvaluator eval = new MathEvaluator(); //basic math double result = eval.Evaluate("(2 + 1) * (1 + 2)"); //calling a function result = eval.Evaluate("sqrt(4)"); //evaluate trigonometric result = eval.Evaluate("cos(pi * 45 / 180.0)"); //convert inches to feet result = eval.Evaluate("12 [in->ft]"); //use variable result = eval.Evaluate("answer * 10"); //add variable eval.Variables.Add("x", 10); result = eval.Evaluate("x * 10"); 

Página de download E é distribuído sob a licença BSD.

Sim, definitivamente é possível ter o usuário digitar C # em uma checkbox de texto, então compilar esse código e executá-lo dentro do seu aplicativo. Fazemos isso no meu trabalho para permitir uma lógica comercial personalizada.

Aqui está um artigo (eu não tenho mais do que skimmed), que deve começar:

http://www.c-sharpcorner.com/UploadFile/ChrisBlake/RunTimeCompiler12052005045037AM/RunTimeCompiler.aspx

Você também pode criar um System.Xml.XPath.XPathNavigator a partir de um stream XML “fictício” vazio e avaliar expressões usando o avaliador XPath:

 static object Evaluate ( string xp ) { return _nav.Evaluate ( xp ); } static readonly System.Xml.XPath.XPathNavigator _nav = new System.Xml.XPath.XPathDocument ( new StringReader ( "" ) ).CreateNavigator ( ); 

Se você deseja registrar variables ​​para usar dentro dessa expressão, você pode criar dinamicamente o XML que pode ser passado na sobrecarga Evaluate que usa um XPathNodeIterator.

  2.151 231.2  

Você pode então escrever expressões como “x / 2 * 0.07914” e, em seguida, x é o valor do nó em seu contexto XML. Outra coisa boa é que você terá access a todas as funções principais do XPath, que incluem matemática e methods de manipulação de strings, e mais coisas.

Se você quiser ir mais longe, você pode até criar seu próprio XsltCustomContext (ou post mal aqui sob demanda) onde você pode resolver referências a funções e variables ​​de extensão:

 object result = Evaluate ( "my:func(234) * $myvar" ); 

my: func é mapeado para um método c # / .net que leva um duplo ou int como parâmetro. myvar é registrado como uma variável dentro do contexto XSLT.

Eu fiz isso usando CSharpCodeProvider, criando a class de placa de boiler e material de function como uma cadeia const dentro da minha class de gerador. Em seguida, insiro o código do usuário na placa da caldeira e compilo.

Era bastante simples de fazer, mas o perigo para essa abordagem é que o usuário que entra na equação pode inserir praticamente qualquer coisa que possa ser um problema de segurança, dependendo da sua aplicação.

Se a segurança é uma preocupação, eu recomendaria o uso de trees de expressão do Lambda, mas se não, usar CSharpCodeProvider é uma opção bastante robusta.

Você já viu http://ncalc.codeplex.com ?

Ele é extensível, rápido (por exemplo, possui seu próprio cache) permite que você forneça funções personalizadas e variables ​​em tempo de execução manipulando events EvaluateFunction / EvaluateParameter. Exemplos de expressões que podem ser analisadas:

 Expression e = new Expression("Round(Pow(Pi, 2) + Pow([Pi2], 2) + X, 2)"); e.Parameters["Pi2"] = new Expression("Pi * Pi"); e.Parameters["X"] = 10; e.EvaluateParameter += delegate(string name, ParameterArgs args) { if (name == "Pi") args.Result = 3.14; }; Debug.Assert(117.07 == e.Evaluate()); 

Ele também lida com unicode e muitos tipos de dados nativamente. Ele vem com um arquivo antler se você quiser alterar a gramática. Existe também um fork que suporta o MEF para carregar novas funções.

Você pode tentar ver o CodeDom ou o Lambda Expression Trees. Eu acho que qualquer um deles deve permitir que você faça isso. As trees de expressão são provavelmente o melhor caminho a percorrer, mas também têm uma curva de aprendizado mais alta.

Você pode começar por aqui e, se realmente quiser entrar, o Boo pode ser modificado para atender às suas necessidades. Você também pode integrar o LUA com o .NET . Qualquer um destes três pode ser utilizado no corpo de um delegado para o seu ConvertEquationToCode .

Tente Vici.Parser: baixe aqui (grátis) , é o analisador / analisador de expressões mais flexível que encontrei até agora.

Se tudo mais falhar, existem classs no namespace System.Reflection.Emit, que você pode usar para produzir novos assemblies, classs e methods.

você pode usar o system.CodeDom para gerar código e compilá-lo na mosca dê uma olhada aqui

Você poderia implementar uma calculadora de pilha postfix . Basicamente, o que você precisa fazer é converter a expressão em notação postfix e simplesmente iterar os tokens no seu postfix para calcular.

Aqui uma biblioteca mais moderna para expressões simples: System.Linq.Dynamic.Core. É compatível com o .NET Standard / .NET Core, está disponível através do NuGet e a fonte está disponível.

https://system-linq-dynamic-core.azurewebsites.net/html/de47654c-7ae4-9302-3061-ea6307706cb8.htm https://github.com/StefH/System.Linq.Dynamic.Core https: // http://www.nuget.org/packages/System.Linq.Dynamic.Core/

Esta é uma biblioteca muito leve e dinâmica.

Eu escrevi uma class wrapper simples para esta biblioteca que me permite fazer coisas como esta:

  string sExpression = "(a == 0) ? 5 : 10"; ExpressionEvaluator exec = new ExpressionEvaluator(sExpression); exec.AddParameter("a", 0); int n0 = exec.Invoke(); 

Uma vez que a expressão é compilada, você pode simplesmente atualizar os valores dos parâmetros e invocar novamente a expressão.

Eu faria uma function recursiva que não escreve código, mas aplica operadores básicos a partes de uma string baseada em caracteres especiais encontrados nessa string. Se mais de um caractere especial for encontrado, ele quebrará a string e chamará essas duas partes.

Eu não sei se é possível implementar sua function ConvertEquationToCode , no entanto, você pode gerar uma estrutura de dados que representa o cálculo que você precisa executar.

Por exemplo, você poderia construir uma tree cujos nós folha representam a input para seu cálculo, cujos nós não-folha representam resultados intermediários e cujo nó raiz representa o cálculo inteiro.

Tem algumas vantagens. Por exemplo, se você estiver fazendo uma análise what-if e quiser alterar o valor de uma input por vez, poderá recalcular os resultados que dependem do valor que você alterou e, ao mesmo tempo, reter os resultados que não foram alterados.