Можно ли скомпилировать и выполнить новый код во время выполнения в .NET?

StackOverflow https://stackoverflow.com/questions/234217

  •  04-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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?

Это было полезно?

Решение

Да!Используя методы, найденные в Microsoft.CSharp, System.CodeDom.Компилятор, и Система.Отражение пространства имен.Вот простое консольное приложение, которое компилирует класс («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();
        }
    }
}

Другие советы

Вы можете попробовать это: 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.

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

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

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

Вы также можете создать 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 " а затем х это значение узла в вашем контексте XML. Еще одна хорошая вещь, у вас будет доступ ко всем основным функциям XPath, который включает в себя математику и методы работы со строками, и многое другое.

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

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

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

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

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

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

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

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

Вы видели 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 & amp; много типов данных изначально. Он поставляется с файлом рога, если вы хотите изменить грамматику. Существует также форк, который поддерживает MEF для загрузки новых функций.

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

Если ничего не помогает, в пространстве имен System.Reflection.Emit есть классы, которые можно использовать для создания новых сборок, классов и методов.

вы можете использовать system.CodeDom для генерации кода и его компиляции на лету посмотрите здесь

Вы можете внедрить калькулятор стеков постфиксов . По сути, вам нужно преобразовать выражение в постфиксную нотацию, а затем просто выполнить итерацию по токенам в вашем постфиксе для вычисления.

Вот более современная библиотека для простых выражений: 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();

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

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

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

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

У него есть некоторые преимущества. Например, если вы выполняете анализ «что, если» и хотите изменить значение одного входа за раз, вы можете пересчитать результаты, которые зависят от значения, которое вы изменили, при этом сохраняя результаты, которые этого не делают.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top