是否有可能在.NET运行时编译和执行新的代码?

注意:mathexpression式评估不是这个问题的重点。 我想在.NET中运行时编译和执行新的代码。 话虽如此…

我想让用户在文本框中input任何等式,如下所示:

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

将这个方程式应用于传入的数据点。 input数据点由x表示,每个数据点由用户指定的等式处理。 我在几年前做过,但是我不喜欢这个解决scheme,因为它需要为每个计算都parsing方程的文本:

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

当你处理数据点的时候,这会带来很多的开销。 我希望能够将这个方程转化为一个函数,这样它就只需要被parsing一次。 它看起来像这样:

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

函数ConvertEquationToCode将parsing方程并返回一个指向适用math函数的指针。

该应用程序将基本上是在运行时编写新的代码。 这可能与.NET?

是! 使用Microsoft.CSharp , System.CodeDom.Compiler和System.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(); } } } 

你可以试试这个: Calculator.Net

它会评估一个mathexpression式。

从发布它将支持以下内容:

 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许可下分发。

是的,绝对有可能让用户inputC#到一个文本框中,然后编译该代码并从你的应用程序中运行它。 我们这样做是为了允许定制业务逻辑。

这里有一篇文章(我没有更多的撇弃它)应该让你开始:

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

您也可以从空的“虚拟”XMLstream创buildSystem.Xml.XPath.XPathNavigator,并使用XPath评估器评估expression式:

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

如果要注册在此expression式中使用的variables,则可以dynamic构buildXML,您可以在采用XPathNodeIterator的Evaluate重载中传递该XML。

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

然后,您可以编写像“x / 2 * 0.07914”这样的expression式,然后x是XML上下文中节点的值。 另一件好事是,您将可以访问所有的XPath核心function,其中包括math和string操作方法,以及更多的东西。

如果你想进一步,你甚至可以build立自己的XsltCustomContext(或者在这里按需提交),你可以在这里parsing对扩展函数和variables的引用:

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

我的:func映射到一个C#/。NET方法,它采用一个双或int作为参数。 myvar在XSLT上下文中被注册为一个variables。

我已经做了这个使用CSharpCodeProvider创build锅炉板类和函数的东西作为一个常量string在我的生成器类。 然后我把用户代码插入锅炉板并编译。

这样做相当简单,但是这种方法的危险在于input等式的用户可能根据您的应用程序input任何可能是安全问题的东西。

如果担心安全问题,我build议使用Lambdaexpression式树,但如果不是,则使用CSharpCodeProvider是相当健壮的选项。

你见过http://ncalc.codeplex.com吗?;

它是可扩展的,快速的(例如有自己的caching)使您能够在运行时通过处理EvaluateFunction / EvaluateParameter事件来提供自定义函数和variables。 它可以parsing的示例expression式:

 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和许多数据types本地。 如果你想改变语法,它带有一个鹿angular文件。 还有一个支持MEF加载新function的分支。

您可以尝试查看CodeDom或Lambdaexpression式树。 我想其中任何一个都可以让你做到这一点。 expression树可能是更好的方法,但也有更高的学习曲线。

你可以从这里开始,如果你真的想进入它, 嘘可以修改,以满足您的需求。 你也可以把LUA和.NET整合在一起 。 其中任何三个都可以在您的ConvertEquationToCode委托的内部使用。

尝试Vici.Parser: 在这里下载(免费) ,这是迄今为止我发现的最灵活的expression式parsing器/评估器。

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

你可以使用system.CodeDom来生成代码,并在飞行中编译它看看这里

你可以实现一个后缀堆栈计算器 。 基本上你所要做的就是将expression式转换为后缀表示法,然后简单地迭代后缀中的标记来计算。

我会做一个recursion函数,不编写代码,而是将基本操作符应用于基于string中的特殊字符的部分string。 如果find多于一个的特殊字符,它将分割string并在这两个部分调用它自己。

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

例如,您可以构build一个叶节点代表您的计算input的树,其非叶节点代表中间结果,其根节点代表整个计算。

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