Можно ли скомпилировать и выполнить новый код во время выполнения в .NET? - PullRequest
33 голосов
/ 24 октября 2008

Примечание. Оценка математических выражений не является целью данного вопроса. Я хочу скомпилировать и выполнить новый код во время выполнения в .NET. При этом ...

Я бы хотел, чтобы пользователь мог ввести любое уравнение, например, следующее, в текстовое поле:

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

И применить это уравнение к входящим точкам данных. Входящие точки данных представлены как x , и каждая точка данных обрабатывается по заданному пользователем уравнению. Я сделал это несколько лет назад, но мне не понравилось решение, потому что для каждого вычисления требовался анализ текста уравнения:

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

Когда вы обрабатываете множество точек данных, это вносит немало накладных расходов. Я хотел бы иметь возможность переводить уравнение в функцию на лету, чтобы ее можно было проанализировать только один раз. Это будет выглядеть примерно так:

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

Функция ConvertEquationToCode будет анализировать уравнение и возвращать указатель на функцию, которая применяет соответствующую математику.

Приложение будет в основном писать новый код во время выполнения. Это возможно с .NET?

Ответы [ 15 ]

18 голосов
/ 05 мая 2012

Да! Использование методов, найденных в пространствах имен Microsoft.CSharp , System.CodeDom.Compiler и System.Reflection . Вот простое консольное приложение, которое компилирует класс ("SomeClass") с одним методом ("Add42") и затем позволяет вам вызывать этот метод. Это простой пример, который я отформатировал так, чтобы полосы прокрутки не появлялись на дисплее кода. Это просто демонстрация компиляции и использования нового кода во время выполнения.

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();
        }
    }
}
13 голосов
/ 24 октября 2008

Вы можете попробовать это: Calculator.Net

Будет оцениваться математическое выражение.

Из публикации будет поддерживаться следующее:

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");

Страница загрузки И распространяется под лицензией BSD.

7 голосов
/ 24 октября 2008

Да, безусловно, можно ввести пользовательский тип C # в текстовое поле, затем скомпилировать этот код и запустить его из своего приложения. Мы делаем это на моей работе, чтобы учесть индивидуальную бизнес-логику.

Вот статья (я только что просмотрел ее), которая должна помочь вам начать:

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

5 голосов
/ 24 октября 2008

Вы также можете создать System.Xml.XPath.XPathNavigator из пустого «фиктивного» потока XML, и оцените выражения с помощью оценщика XPath:

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

Если вы хотите зарегистрировать переменные для использования в этом выражении, вы можете динамически создавать XML, который вы можете передать в перегрузке Evaluate для этого требуется XPathNodeIterator.

<context>
  <x>2.151</x>
  <y>231.2</y>
</context>

Затем вы можете написать такие выражения, как "x / 2 * 0.07914", а затем x это значение узла в вашем контексте XML. Еще одна хорошая вещь, у вас будет доступ ко всем основным функциям XPath, который включает в себя математику и методы работы со строками, и многое другое.

Если вы хотите пойти дальше, вы даже можете создать свой собственный XsltCustomContext (или постить здесь по требованию) где вы можете разрешить ссылки на функции расширения и переменные:

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

my: func отображается на метод C # /. NET, который принимает в качестве параметра значение типа double или int. myvar зарегистрирован как переменная в контексте XSLT.

3 голосов
/ 24 октября 2008

Я сделал это с помощью CSharpCodeProvider, создав класс рабочей плиты и функциональные элементы в виде строки const внутри моего класса генератора. Затем я вставляю код пользователя в плиту котла и компилирую.

Это было довольно просто сделать, но опасность этого подхода в том, что пользователь, вводящий уравнение, может ввести практически все, что может быть проблемой безопасности, в зависимости от вашего приложения.

Если безопасность вообще важна, я бы порекомендовал использовать деревья лямбда-выражений, но если нет, то использование CSharpCodeProvider - достаточно надежный вариант.

2 голосов
/ 10 августа 2011

Вы видели http://ncalc.codeplex.com?

Это расширяемое, быстрое (например, имеет собственный кэш) позволяет вам предоставлять пользовательские функции и переменные во время выполнения путем обработки событий EvaluateFunction / EvaluateParameter. Примеры выражений, которые он может анализировать:

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()); 

Он также обрабатывает Unicode и многие типы данных изначально. Он поставляется с файлом рога, если вы хотите изменить грамматику. Существует также форк, который поддерживает MEF для загрузки новых функций.

2 голосов
/ 24 октября 2008

Вы можете начать здесь , и если вы действительно хотите в него войти, Boo можно изменить в соответствии с вашими потребностями. Вы также можете интегрировать LUA с .NET . Любые три из них могут быть использованы в теле делегата для вашего ConvertEquationToCode.

2 голосов
/ 24 октября 2008

Вы можете попробовать просмотреть деревья выражений CodeDom или Lambda. Я думаю, что любой из них должен позволить вам достичь этого. Деревья выражений, вероятно, лучший путь, но также имеют более высокую кривую обучения.

1 голос
/ 17 сентября 2009

Попробуйте Vici.Parser: скачайте его здесь (бесплатно) , это самый гибкий анализатор / оценщик выражений, который я когда-либо встречал.

0 голосов
/ 20 мая 2018

Вот более современная библиотека для простых выражений: System.Linq.Dynamic.Core. Он совместим с .NET Standard / .NET Core, доступен через NuGet, а источник доступен.

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

Это очень легкая и динамичная библиотека.

Я написал простой класс-оболочку для этой библиотеки, который позволяет мне делать такие вещи:

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

После того как выражение скомпилировано, вы можете просто обновить значения параметров и повторно вызвать выражение.

...