注:数式の評価はこの質問の焦点ではありません。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...
}
大量のデータポイントを処理している場合、これによりかなりのオーバーヘッドが発生します。方程式をその場で関数に変換できるようにしたいので、1回だけ解析する必要があります。次のようになります。
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.Compiler 、および System.Reflection ネームスペースにあるメソッドを使用します。これは、1つのメソッド( "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
数式を評価します。
投稿から、それは以下をサポートします:
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#と入力し、そのコードをコンパイルしてアプリ内から実行することは間違いなく可能です。カスタムビジネスロジックを可能にするために、私の仕事でそれを行います。
これがあなたが始めるべき記事です(私はそれをざっと読んだだけです):
空の「ダミー」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 ( );
この式内で使用する変数を登録する場合は、XPathNodeIteratorを受け取るEvaluateオーバーロードで渡すことができるXMLを動的に構築できます。
<context>
<x>2.151</x>
<y>231.2</y>
</context>
次に、「x/2 * 0.07914」のような式を記述できます。その場合、xはXMLコンテキスト内のノードの値です。もう1つの良い点は、数学や文字列操作メソッドなど、すべてのXPathコア関数にアクセスできることです。
さらに詳しく知りたい場合は、独自のXsltCustomContextを作成して(またはオンデマンドでここに投稿してください)、拡張関数と変数への参照を解決することもできます。
object result = Evaluate ( "my:func(234) * $myvar" );
my:funcは、パラメーターとしてdoubleまたはintを受け取るC#/。NETメソッドにマップされます。 myvarは、XSLTコンテキスト内で変数として登録されます。
これは、CSharpCodeProviderを使用して、ジェネレータークラス内にconst文字列としてボイラープレートクラスと関数を作成することで行いました。次に、ユーザーコードをボイラープレートに挿入してコンパイルします。
行うのはかなり簡単でしたが、このアプローチの危険性は、方程式を入力するユーザーが、アプリケーションによってはセキュリティの問題になる可能性のあるほぼすべてのものを入力できることです。
セキュリティがまったく懸念される場合は、Lambda式ツリーを使用することをお勧めしますが、そうでない場合は、CSharpCodeProviderを使用することはかなり堅牢なオプションです。
CodeDomまたはLambda式ツリーのいずれかを見てみることができます。どちらか1つでこれを達成できるはずだと思います。式ツリーはおそらくより良い方法ですが、学習曲線も高くなります。
見たことがありますか 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をサポートするフォークもあります。
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.htmhttps://github.com/StefH/ System.Linq.Dynamic.Corehttps://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();
式がコンパイルされたら、パラメータ値を更新して式を再度呼び出すことができます。