Czy można skompilować i wykonać nowy kod w czasie wykonywania in.NET?

Uwaga: ocena wyrażeń matematycznych nie jest przedmiotem tego pytania. Chcę skompilować i wykonać nowy kod w środowisku uruchomieniowym w .NET. to powiedziane...

Chciałbym, aby użytkownik mógł wprowadzić dowolne równanie, jak poniżej, w polu tekstowym:

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

I zastosuj to równanie do przychodzących punktów danych. Przychodzące punkty danych są reprezentowane przez x , a każdy punkt danych jest przetwarzany przez równanie określone przez użytkownika. Tak. to było lata temu, ale nie podobało mi się rozwiązanie, ponieważ wymagało ono parsowania tekstu równania dla każdego obliczenia:

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

Gdy przetwarzasz ładunki punktów danych, wprowadza to sporo kosztów ogólnych. Chciałbym być w stanie przetłumaczyć równanie na funkcję, w locie, tak, że musi być parsowane tylko raz. Wyglądałoby to mniej więcej tak:

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

Funkcja ConvertEquationToCode analizuje równanie i zwraca wskaźnik do funkcji to stosuje odpowiednią matematykę.

Aplikacja będzie w zasadzie pisać nowy kod w czasie uruchomienia. Czy jest to możliwe z. Net?

Author: raven, 2008-10-24

15 answers

Tak! Korzystanie z metod znalezionych w Microsoft.CSharp, System.CodeDom.Kompilator i system .Reflection spacje nazw. Oto prosta aplikacja konsolowa, która kompiluje klasę ("SomeClass") z jedną metodą ("Add42"), a następnie pozwala na wywołanie tej metody. Jest to przykład, który sformatowałem w dół, aby zapobiec pojawianiu się pasków przewijania na wyświetlaczu kodu. Ma to na celu zademonstrowanie kompilacji i używania nowego kodu w czasie wykonywania.

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();
        }
    }
}
 19
Author: raven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-13 11:42:10

Możesz spróbować tego: Calculator.Net

Oceni wyrażenie matematyczne.

From the posting it will support the following:

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

Strona Pobierania I jest rozpowszechniany na licencji BSD.

 13
Author: Brian Schmitt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 16:39:21

Tak, zdecydowanie możliwe, aby użytkownik wpisał C# w polu tekstowym, a następnie skompilował ten kod i uruchomił go z poziomu aplikacji. Robimy to w mojej pracy, aby umożliwić niestandardową logikę biznesową.

Oto artykuł (nie przejrzałem go), który powinien zacząć:

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

 7
Author: rice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 16:24:04

Można również utworzyć System.Xml.XPath.XPathNavigator z pustego, "atrapowego" strumienia XML, i oceniaj wyrażenia za pomocą ewaluatora 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 ( );

Jeśli chcesz zarejestrować zmienne do użycia w tym wyrażeniu, możesz dynamicznie budować XML, który możesz przekazać w ocenie przeciążenia to wymaga XPathNodeIterator.

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

Możesz wtedy napisać wyrażenia typu "x / 2 * 0.07914", a następnie x jest wartością węzła w kontekście XML. Inną dobrą rzeczą jest to, że będziesz mieć dostęp do wszystkich podstawowych funkcji XPath, który obejmuje matematykę, metody manipulacji strunami i wiele innych rzeczy.

Jeśli chcesz pójść dalej, możesz nawet zbudować swój własny XsltCustomContext(lub napisać tutaj na żądanie) gdzie można rozwiązywać odwołania do funkcji i zmiennych rozszerzenia:

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

My: func jest mapowane do metody C#/. Net, która przyjmuje jako parametr double lub int. myvar jest zarejestrowany jako zmienna w kontekście XSLT.

 5
Author: baretta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 18:12:15

Możesz spróbować spojrzeć na drzewa wyrażeń CodeDom lub Lambda. Myślę, że jedno z nich powinno pozwolić ci to osiągnąć. Drzewa ekspresji są prawdopodobnie lepszym sposobem, ale mają również wyższą krzywą uczenia się.

 3
Author: Scott Dorman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 16:21:30

Zrobiłem to używając CSharpCodeProvider, tworząc klasę boiler plate i function stuff jako const string wewnątrz mojej klasy generatora. Następnie wstawiam kod użytkownika do płyty kotła i kompiluję.

Było to dość proste do zrobienia, ale niebezpieczeństwo dla tego podejścia jest to, że użytkownik wprowadzający równanie może wprowadzić prawie wszystko, co może być problemem bezpieczeństwa w zależności od aplikacji.

Jeśli bezpieczeństwo jest w ogóle problemem, polecam użycie Lambda Drzewa wyrażeń, ale jeśli nie, użycie CSharpCodeProvider jest dość solidną opcją.

 3
Author: Jon Norton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 16:54:45

Możesz zacząć Tutaj i jeśli naprawdę chcesz się do tego zabrać, Boo można zmodyfikować do swoich potrzeb. Możesz również zintegrować LUA z. NET . dowolne trzy z nich mogą być wykorzystane w ciele delegata dla twojego ConvertEquationToCode.

 2
Author: cfeduke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 16:23:43

Czy widziałeś http://ncalc.codeplex.com ?

Jest rozszerzalny, szybki (np. posiada własny cache) pozwala na dostarczanie niestandardowych funkcji i zmiennych w czasie wykonywania przez obsługę EvaluateFunction / EvaluateParameter zdarzeń. Przykładowe wyrażenia, które może analizować:

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

Obsługuje również Unicode i wiele typów danych natywnie. Jest wyposażony w plik poroża, jeśli chcesz zmienić grammer. Istnieje również widelec, który obsługuje MEF do ładowania nowych funkcji.

 2
Author: GreyCloud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-10 15:07:40

Spróbuj Vici.Parser: pobierz go tutaj (za darmo) , jest to najbardziej elastyczny parser/ewaluator wyrażeń, jaki do tej pory znalazłem.

 1
Author: Roel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-17 12:46:35

Tutaj bardziej nowoczesna biblioteka dla prostych wyrażeń: System. Linq.Dynamiczny.Rdzeń. Jest kompatybilny z. NET Standard/. Net Core, jest dostępny przez NuGet, a źródło jest dostępne.

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/

Jest to bardzo lekki i dynamiczny biblioteka.

Napisałem prostą klasę wrapper dla tej biblioteki, która pozwala mi robić takie rzeczy:

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

Po skompilowaniu wyrażenia można po prostu zaktualizować wartości parametrów i ponownie wywołać wyrażenie.

 1
Author: Kent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-20 01:41:24

Jeśli Wszystko inne zawiedzie, w systemie są klasy.Odbicie.Emituj przestrzeń nazw, której możesz używać do tworzenia nowych złożeń, klas i metod.

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 19:56:48

Możesz użyć system.CodeDom aby wygenerować kod i skompilować go w locie zobacz Tutaj

 0
Author: Hannoun Yassir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-04 00:52:14

Możesz zaimplementować Kalkulator stosu postfix. Zasadniczo to, co musisz zrobić, to przekonwertować wyrażenie na notację postfixa, a następnie po prostu iterować tokeny w swoim postfixie, aby obliczyć.

 0
Author: Erik van Brakel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-04 00:58:00

Wykonałbym funkcję rekurencyjną, która nie pisze kodu, ale zamiast tego stosuje podstawowe operatory do części łańcucha znaków opartych na specjalnych znakach znalezionych w tym łańcuchu. Jeśli znajdzie się więcej niż jeden znak specjalny, zrywa łańcuch i wywołuje się na tych dwóch częściach.

 -1
Author: Totty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-24 16:23:32

Nie wiem, czy można zaimplementować swoją funkcję ConvertEquationToCode, jednak można wygenerować strukturę danych, która reprezentuje obliczenia, które trzeba wykonać.

Na przykład można zbudować drzewo, którego węzły liściowe reprezentują dane wejściowe do obliczeń, których węzły bez liści reprezentują wyniki pośrednie, a których węzeł korzeniowy reprezentuje całe obliczenia.

Ma pewne zalety. Na przykład, jeśli robisz analizę what-if I chcesz zmienić wartość jednego wprowadzanie danych na raz, można ponownie obliczyć wyniki, które zależą od wartości, które zostały zmienione, zachowując wyniki, które nie.
 -2
Author: pyon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-17 19:29:02