C#コードを動的に評価するにはどうすればよいですか?


92

eval("something()");JavaScriptでコードを動的に実行することができます。C#で同じことをする方法はありますか?

私がやろうとしていることの例は次のとおりです。整数変数(たとえばi)があり、 "Property1"、 "Property2"、 "Property3"などの名前で複数のプロパティがあります。ここで、いくつかの操作を実行したいと思いますの値に応じて「プロパティi」プロパティにi

これはJavascriptを使うと本当に簡単です。これをC#で行う方法はありますか?



2
c#ironpythonのevalを呼び出します。C#4.0で試してみました。c#2.0の経験なし
Peter Long

@Peter Long、IronPythonの評価に関するドキュメントはどこにありますか?
smartcaveman 2011年

@AdhipGupta私はこのQ&Aがかなり古くなっていることを知っていますが、Davide Icardiの回答で与えられた説明のように聞こえるビデオ再生リストをリリースしました。それは劇的に異なり、おそらくチェックする価値があります。
リックリッグス

回答:


49

残念ながら、C#はそのような動的言語ではありません。

ただし、できることは、クラスなどすべてが含まれたC#ソースコードファイルを作成し、C#のCodeDomプロバイダーを介して実行し、アセンブリにコンパイルして実行することです。

このMSDNのフォーラム投稿には、ページの下にいくつかのサンプルコードを含む回答が含ま
れています。文字列から匿名メソッドを作成しますか?

これが非常に良い解決策であるとは言い難いですが、とにかくそれは可能です。

その文字列では、どのようなコードが期待されますか?それが有効なコードのマイナーサブセットである場合、たとえば単なる数式の場合、他の代替手段が存在する可能性があります。


編集:まあ、それは私が最初に徹底的に質問を読むことを教えてくれます。はい、反射はここであなたにいくつかの助けを与えることができるでしょう。

文字列を;で分割した場合 最初に、個々のプロパティを取得するには、次のコードを使用してクラスの特定のプロパティのPropertyInfoオブジェクトを取得し、次にそのオブジェクトを使用して特定のオブジェクトを操作します。

String propName = "Text";
PropertyInfo pi = someObject.GetType().GetProperty(propName);
pi.SetValue(someObject, "New Value", new Object[0]);

リンク:PropertyInfo.SetValueメソッド


GetPropertyがx、y paramsの関数である必要がある場合、Text(1,2)を意味しますか?
user1735921

@ user1735921次にGetMethod(methodName)、代わりにを使用して、パラメーター値を解析し、リフレクションを使用してメソッドを呼び出す必要があります。
Lasse V. Karlsen

typeof(ObjectType)はsomeObject.GetType()に類似しています
Thiago

32

RoslynスクリプトAPIの使用(その他のサンプルはこちら):

// add NuGet package 'Microsoft.CodeAnalysis.Scripting'
using Microsoft.CodeAnalysis.CSharp.Scripting;

await CSharpScript.EvaluateAsync("System.Math.Pow(2, 4)") // returns 16

また、任意のコードを実行することもできます。

var script = await CSharpScript.RunAsync(@"
                class MyClass
                { 
                    public void Print() => System.Console.WriteLine(1);
                }")

以前の実行で生成されたコードを参照します。

await script.ContinueWithAsync("new MyClass().Print();");

14

あんまり。リフレクションを使用して目的を達成できますが、JavaScriptほど単純ではありません。たとえば、オブジェクトのプライベートフィールドを何かに設定したい場合は、次の関数を使用できます。

protected static void SetField(object o, string fieldName, object value)
{
   FieldInfo field = o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
   field.SetValue(o, value);
}

11

これは、c#でのeval関数です。文字列から無名関数(ラムダ式)を変換するために使用しました。出典:http : //www.codeproject.com/KB/cs/evalcscode.aspx

public static object Eval(string sCSCode) {

  CSharpCodeProvider c = new CSharpCodeProvider();
  ICodeCompiler icc = c.CreateCompiler();
  CompilerParameters cp = new CompilerParameters();

  cp.ReferencedAssemblies.Add("system.dll");
  cp.ReferencedAssemblies.Add("system.xml.dll");
  cp.ReferencedAssemblies.Add("system.data.dll");
  cp.ReferencedAssemblies.Add("system.windows.forms.dll");
  cp.ReferencedAssemblies.Add("system.drawing.dll");

  cp.CompilerOptions = "/t:library";
  cp.GenerateInMemory = true;

  StringBuilder sb = new StringBuilder("");
  sb.Append("using System;\n" );
  sb.Append("using System.Xml;\n");
  sb.Append("using System.Data;\n");
  sb.Append("using System.Data.SqlClient;\n");
  sb.Append("using System.Windows.Forms;\n");
  sb.Append("using System.Drawing;\n");

  sb.Append("namespace CSCodeEvaler{ \n");
  sb.Append("public class CSCodeEvaler{ \n");
  sb.Append("public object EvalCode(){\n");
  sb.Append("return "+sCSCode+"; \n");
  sb.Append("} \n");
  sb.Append("} \n");
  sb.Append("}\n");

  CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
  if( cr.Errors.Count > 0 ){
      MessageBox.Show("ERROR: " + cr.Errors[0].ErrorText, 
         "Error evaluating cs code", MessageBoxButtons.OK, 
         MessageBoxIcon.Error );
      return null;
  }

  System.Reflection.Assembly a = cr.CompiledAssembly;
  object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");

  Type t = o.GetType();
  MethodInfo mi = t.GetMethod("EvalCode");

  object s = mi.Invoke(o, null);
  return s;

}

1
@seheおっと、タイプミスを修正しました(Lambada => Lambda)。この曲がLambadaと呼ばれていることを知らなかったので、これは意図的ではありませんでした。;)
Largo

なぜこの回答が投票を減らすのか理解できませんでした。とても便利です。
Muzaffer Galata 2018

9

私は、C#構文を使用して記述されたテキスト式をデリゲート(または式ツリー)に変換できるオープンソースプロジェクトDynamic Expressoを作成しました。式はコンパイルまたはリフレクションを使用せずに解析され、式ツリーに変換されます。

次のように書くことができます:

var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");

または

var interpreter = new Interpreter()
                      .SetVariable("service", new ServiceExample());

string expression = "x > 4 ? service.SomeMethod() : service.AnotherMethod()";

Lambda parsedExpression = interpreter.Parse(expression, 
                          new Parameter("x", typeof(int)));

parsedExpression.Invoke(5);

私の仕事はスコット・グーの記事http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspxに基づいています


7

これらはすべて間違いなく機能します。個人的には、その特定の問題に対して、私はおそらく少し異なるアプローチを取るでしょう。多分このようなもの:

class MyClass {
  public Point point1, point2, point3;

  private Point[] points;

  public MyClass() {
    //...
    this.points = new Point[] {point1, point2, point3};
  }

  public void DoSomethingWith(int i) {
    Point target = this.points[i+1];
    // do stuff to target
  }
}

このようなパターンを使用する場合、データは値ではなく参照によって格納されることに注意する必要があります。つまり、プリミティブでこれを行わないでください。あなたは彼らの大きな肥大したクラスの対応物を使わなければなりません。

それは問題ではないことに気づきましたが、問題はかなりよく答えられており、別のアプローチが役立つかもしれないと思いました。


5

C#ステートメントを絶対に実行したい場合は今はしませんが、C#2.0でJavascriptステートメントをすでに実行できます。オープンソースのライブラリであるJintはそれを行うことができます。.NETのJavascriptインタプリタです。JavaScriptプログラムを渡すと、アプリケーション内で実行されます。C#オブジェクトを引数として渡し、それを自動化することもできます。

また、プロパティの式を評価するだけの場合は、NCalcを試してください


3

リフレクションを使用してプロパティを取得し、それを呼び出すことができます。このようなもの:

object result = theObject.GetType().GetProperty("Property" + i).GetValue(theObject, null);

つまり、プロパティを持つオブジェクトが「theObject」と呼ばれていると仮定します:)


2

また、Webブラウザーを実装してから、JavaScriptを含むHTMLファイルをロードすることもできます。

次にdocument.InvokeScript、このブラウザでメソッドに移動します。eval関数の戻り値をキャッチして、必要なものすべてに変換できます。

私はいくつかのプロジェクトでこれを行い、それは完全に機能します。

それが役に立てば幸い


0

あなたはプロトタイプ関数でそれを行うことができます:

void something(int i, string P1) {
    something(i, P1, String.Empty);
}

void something(int i, string P1, string P2) {
    something(i, P1, P2, String.Empty);
}

void something(int i, string P1, string P2, string P3) {
    something(i, P1, P2, P3, String.Empty);
}

等々...



0

コードを動的にコンパイルおよび実行するタスクを簡素化するために、SharpByte.Dynamicというパッケージを作成しました。ここでさらに詳しく説明するように、拡張メソッドを使用して、任意のコンテキストオブジェクトでコードを呼び出すことができます

例えば、

someObject.Evaluate<int>("6 / {{{0}}}", 3))

3を返します。

someObject.Evaluate("this.ToString()"))

コンテキストオブジェクトの文字列表現を返します。

someObject.Execute(@
"Console.WriteLine(""Hello, world!"");
Console.WriteLine(""This demonstrates running a simple script"");
");

これらのステートメントをスクリプトなどとして実行します。

実行可能ファイルは、ここの例に示すように、ファクトリメソッドを使用して簡単に取得できます。必要なのは、ソースコードと予想される名前付きパラメーターのリストだけです(トークンは、{{{0}}などの三重括弧表記を使用して埋め込まれますstring.Format()およびハンドルバーのような構文との衝突を避けるために):

IExecutable executable = ExecutableFactory.Default.GetExecutable(executableType, sourceCode, parameterNames, addedNamespaces);

各実行可能オブジェクト(スクリプトまたは式)はスレッドセーフであり、保存および再利用が可能で、スクリプト内からのロギングをサポートし、タイミング情報および発生した場合は最後の例外を保存します。また、それぞれにコンパイルされたCopy()メソッドもあります。安価なコピーを作成する。つまり、スクリプトまたは式からコンパイルされた実行可能オブジェクトをテンプレートとして使用して、他のオブジェクトを作成します。

コンパイル済みのスクリプトまたはステートメントの実行のオーバーヘッドは比較的低く、適度なハードウェアではマイクロ秒未満であり、コンパイル済みのスクリプトと式は再利用のためにキャッシュされます。


0

名前で構造体(クラス)メンバーの値を取得しようとしました。構造は動的ではありませんでした。私が最終的にそれを得るまで、すべての答えはうまくいきませんでした:

public static object GetPropertyValue(object instance, string memberName)
{
    return instance.GetType().GetField(memberName).GetValue(instance);
}

このメソッドは、名前でメンバーの値を返します。通常の構造(クラス)で動作します。


0

Heleonix.Reflectionライブラリーを確認してください。ネストされたメンバーを含め、メンバーを動的に取得/設定/呼び出すメソッドを提供します。または、メンバーが明確に定義されている場合は、リフレクションよりも速いゲッター/セッター(デリゲートにコンパイルされたラムダ)を作成できます。

var success = Reflector.Set(instance, null, $"Property{i}", value);

または、プロパティの数が無限でない場合は、セッターを生成してそれらをチェックできます(セッターはコンパイルされたデリゲートであるため、高速です)。

var setter = Reflector.CreateSetter<object, object>($"Property{i}", typeof(type which contains "Property"+i));
setter(instance, value);

セッターはタイプにすることができますがAction<object, object>、インスタンスは実行時に異なる場合があるため、セッターのリストを作成できます。


-2

残念ながら、C#には、ユーザーが求めていることを正確に実行するためのネイティブ機能がありません。

ただし、私のC#評価プログラムでは、C#コードを評価できます。実行時にC#コードを評価し、多くのC#ステートメントをサポートします。実際、このコードは.NETプロジェクト内で使用できますが、C#構文の使用に限定されています。詳細については、私のウェブサイトhttp://csharp-eval.comをご覧ください。



-9

正解は、mem0ryの使用率を低く保つためにすべての結果をキャッシュする必要があることです。

例は次のようになります

TypeOf(Evaluate)
{
"1+1":2;
"1+2":3;
"1+3":5;
....
"2-5":-3;
"0+0":1
} 

リストに追加します

List<string> results = new List<string>();
for() results.Add(result);

IDを保存してコードで使用する

お役に立てれば


5
誰かが評価とルックアップを混同しました。あなたはすべての可能なプログラムを(私が知っていればと思うことがあり、少なくとも NP-ハード)...と、あなたはすべての可能な結果をプリコンパイルするsupermachineを持っている... 何の副作用/外部入力がない...うん、このアイデアは、理論的に動作します。コードは、1つの大きな構文エラー、しかしである
sehe
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.