35

注: 数式の評価は、この質問の焦点では​​ありません。.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 で可能ですか?

4

15 に答える 15

19

はい!Microsoft.CSharpSystem.CodeDom.Compiler、およびSystem.Reflection名前空間にあるメソッドを使用します。以下は、クラス (「SomeClass」) を 1 つのメソッド (「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();
        }
    }
}
于 2012-05-04T23:12:05.990 に答える
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");

ダウンロードページ BSD ライセンスの下で配布されています。

于 2008-10-24T16:39:21.630 に答える
7

はい、ユーザーが C# をテキスト ボックスに入力し、そのコードをコンパイルして、アプリ内から実行することは間違いなく可能です。私の仕事では、カスタム ビジネス ロジックを可能にするためにこれを行っています。

これは、あなたが始めるための記事です(私はそれをざっと読んだだけです)。

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

于 2008-10-24T16:24:04.530 に答える
6

また、空の「ダミー」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 コンテキスト内の変数として登録されます。

于 2008-10-24T18:12:15.357 に答える
3

CSharpCodeProvider を使用して、ボイラー プレート クラスと関数をジェネレーター クラス内の const 文字列として作成することにより、これを行いました。次に、ボイラー プレートにユーザー コードを挿入してコンパイルします。

実行するのはかなり簡単でしたが、このアプローチの危険性は、数式を入力するユーザーが、アプリケーションによってはセキュリティ上の問題となる可能性のあるほぼすべてのものを入力できることです。

セキュリティがまったく問題になる場合は、ラムダ式ツリーを使用することをお勧めしますが、そうでない場合は、CSharpCodeProvider を使用するのがかなり堅牢なオプションです。

于 2008-10-24T16:54:45.497 に答える
3

CodeDom または Lambda 式ツリーのいずれかを調べることができます。これらのいずれかでこれを達成できるようにする必要があると思います。表現ツリーはおそらくより良い方法ですが、学習曲線も高くなります。

于 2008-10-24T16:21:30.340 に答える
3

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 と多くのデータ型をネイティブに処理します。グラマーを変更したい場合は、antler ファイルが付属しています。新しい機能をロードするために MEF をサポートするフォークもあります。

于 2011-08-10T15:07:40.427 に答える
2

ここから始めて、本当にやりたい場合は、ニーズに合わせてBooを変更できます。LUA を .NET と統合することもできます。これらのうち 3 つをデリゲートの本体内で使用できますConvertEquationToCode

于 2008-10-24T16:23:43.720 に答える
1

Vici.Parser を試してみてください:ダウンロードはこちら (無料)です。これは、私がこれまでに見つけた中で最も柔軟な式パーサー/エバリュエーターです。

于 2009-09-17T12:46:35.680 に答える
0

後置スタック計算機を実装できます。基本的に、式を後置表記に変換し、後置のトークンを反復して計算するだけです。

于 2010-02-04T00:58:00.663 に答える
0

他のすべてが失敗した場合、新しいアセンブリ、クラス、およびメソッドを作成するために使用できる System.Reflection.Emit 名前空間の下にクラスがあります。

于 2008-10-24T19:56:48.500 に答える
-1

you can use system.CodeDom to generate code and compile it on the fly have a look here

于 2010-02-04T00:52:14.513 に答える
-1

コードを記述しない再帰関数を実行しますが、その代わりに、その文字列で見つかった特殊文字に基づいて文字列の一部に基本的な演算子を適用します。複数の特殊文字が見つかった場合、文字列を分割し、それらの 2 つの部分で自分自身を呼び出します。

于 2008-10-24T16:23:32.317 に答える
-2

関数を実装できるかどうかはわかりませんがConvertEquationToCode、実行する必要がある計算を表すデータ構造を生成できます。

たとえば、リーフ ノードが計算の入力を表し、非リーフ ノードが中間結果を表し、ルート ノードが計算全体を表すツリーを構築できます。

それにはいくつかの利点があります。たとえば、what-if 分析を行っていて、一度に 1 つの入力の値を変更したい場合、変更した値に依存する結果を再計算し、変更していない結果を保持することができます。

于 2008-12-17T19:29:02.947 に答える