是否可以在.NET中运行时编译和执行新代码?

时间:2008-10-24 16:18:26

标签: c# .net compilation runtime

注意:数学表达式评估不是这个问题的焦点。我想在.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 个答案:

答案 0 :(得分:18)

是的!使用Microsoft.CSharpSystem.CodeDom.CompilerSystem.Reflection名称空间中的方法。这是一个简单的控制台应用程序,它使用一种方法(“Add42”)编译一个类(“SomeClass”),然后允许您调用该方法。这是一个简单的例子,我格式化以防止滚动条出现在代码显示中。它只是演示在运行时编译和使用新代码。

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

答案 1 :(得分:13)

您可以尝试这样做: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");

Download Page 并根据BSD许可证分发。

答案 2 :(得分:7)

是的,绝对可以让用户在文本框中键入C#,然后编译该代码并在您的应用程序中运行它。我们在工作中这样做是为了允许自定义业务逻辑。

这是一篇文章(我只是略读),它应该让你开始:

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

答案 3 :(得分:5)

您还可以从空的“虚拟”XML流创建System.Xml.XPath.XPathNavigator, 并使用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 ( );

如果要注册要在此表达式中使用的变量, 您可以动态构建可以在Evaluate重载中传递的XML 需要一个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上下文中注册为变量。

答案 4 :(得分:3)

我使用CSharpCodeProvider通过在我的生成器类中创建锅炉板类和函数作为const字符串来完成此操作。然后我将用户代码插入到锅炉板中并进行编译。

这样做非常简单,但这种方法的危险在于输入等式的用户可能会输入任何可能存在安全问题的内容,具体取决于您的应用程序。

如果担心安全问题,我建议使用Lambda Expression Trees,但如果没有,使用CSharpCodeProvider是一个非常强大的选择。

答案 5 :(得分:2)

您可以尝试查看CodeDom或Lambda表达式树。我认为其中任何一个应该允许你完成这个。表达树可能是更好的方法,但也有更高的学习曲线。

答案 6 :(得分:2)

你可以开始here,如果你真的想进入它,可以修改Boo以满足你的需求。您还可以整合LUA with .NET。其中任何三个都可以在代理人的身体内使用ConvertEquationToCode

答案 7 :(得分:2)

你见过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加载新功能的fork。

答案 8 :(得分:1)

尝试Vici.Parser:download it here (free),它是迄今为止我发现的最灵活的表达式解析器/评估器。

答案 9 :(得分:0)

如果所有其他方法都失败了,System.Reflection.Emit命名空间下的类可以用来生成新的程序集,类和方法。

答案 10 :(得分:0)

您可以使用system.CodeDom生成代码并动态编译 看看here

答案 11 :(得分:0)

您可以实施postfix stack calculator。基本上你要做的就是将表达式转换为后缀表示法,然后简单地迭代你的后缀中的标记来计算。

答案 12 :(得分:0)

这是一个更简单的表达式的现代库: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();

编译表达式后,您只需更新参数值并重新调用表达式。

答案 13 :(得分:-1)

我会做一个递归函数,它不会编写代码,而是根据该字符串中的特殊字符将基本运算符应用于字符串的某些部分。 如果找到多个特殊字符,它会分解字符串并在这两个部分上调用自己。

答案 14 :(得分:-2)

我不知道是否可以实现您的ConvertEquationToCode功能,但是,您可以生成一个代表您需要执行的计算的数据结构。

例如,您可以构建一个树,其叶节点表示计算的输入,其非叶节点表示中间结果,其根节点表示整个计算。

它有一些优点。例如,如果您正在进行假设分析并希望一次更改一个输入的值,则可以重新计算取决于您已更改的值的结果,同时保留不具有的结果。 / p>