web-dev-qa-db-ja.com

.NETで実行時に新しいコードをコンパイルして実行することは可能ですか?

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

33
raven

はい! Microsoft.CSharpSystem.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();
        }
    }
}
18
raven

あなたはこれを試すかもしれません: 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ライセンスの下で配布されています。

13
Brian Schmitt

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

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

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

7
rice

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

5
baretta

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

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

セキュリティがまったく懸念される場合は、Lambda式ツリーを使用することをお勧めしますが、そうでない場合は、CSharpCodeProviderを使用することはかなり堅牢なオプションです。

3
Jon Norton

CodeDomまたはLambda式ツリーのいずれかを見てみることができます。どちらか1つでこれを達成できるはずだと思います。式ツリーはおそらくより良い方法ですが、学習曲線も高くなります。

3
Scott Dorman

見たことがありますか 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をサポートするフォークもあります。

2
GreyCloud

あなたは ここ を始めることができます、そしてあなたが本当にそれに入りたいならば、 ブー あなたのニーズを満たすために修正することができます。 LUAと.NET を統合することもできます。これらの3つは、ConvertEquationToCodeのデリゲートの本文内で利用できます。

2
cfeduke

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

1
Roel

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

0
Tim

system.CodeDomを使用してコードを生成し、その場でコンパイルすることができます ここ

0
Hannoun Yassir

接尾辞スタック計算機 を実装できます。基本的には、式を接尾辞表記に変換してから、接尾辞のトークンを繰り返し処理して計算するだけです。

0
Erik van Brakel

ここに、単純な式のためのより現代的なライブラリがあります: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();

式がコンパイルされたら、パラメータ値を更新して式を再度呼び出すことができます。

0
Kent