如何动态评估C#代码?


92

我可以做一个eval("something()");在JavaScript中动态执行代码。有没有办法让我在C#中做同样的事情?

我要尝试执行的一个示例是:我有一个整数变量(例如i),并且具有多个具有以下名称的属性:“ Property1”,“ Property2”,“ Property3”等。现在,我想执行一些操作在“ Property i ”属性上取决于值i

使用Javascript确实很简单。有什么办法用C#做到这一点?



2
c#调用ironpython的eval。我在c#4.0中尝试过。没有使用c#2.0的经验
Peter Long

@Peter Long,在哪里可以找到有关IronPython评估的文档?
smartcaveman 2011年

@AdhipGupta我知道这个问答已经过时了,但是我刚刚发布了一个视频播放列表,听起来很像戴维·伊卡迪(Davide Icardi)的回答。这是完全不同的,可能值得一试。
里克·里格斯

Answers:


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函数。我用它来从字符串转换匿名函数(Lambda表达式)。资料来源: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 '18

9

我已经编写了一个开源项目Dynamic Expresso,它可以将使用C#语法编写的文本表达式转换为委托(或表达式树)。无需使用编译或反射就可以将表达式解析并转换为表达式树

您可以这样写:

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

我的工作基于Scott Gu的文章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
  }
}

使用这种模式时,必须注意数据是按引用而不是按值存储的。换句话说,不要对基元执行此操作。您必须使用他们big肿的同类。

我意识到这不完全是问题,但是这个问题已经很好地回答了,我认为也许有替代方法可能会有所帮助。


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

您还可以实现Webbrowser,然后加载包含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"");
");

将这些语句作为脚本运行,等等。

可执行文件可以很容易地利用工厂的方法来得到,如在例子中所看到这里 --all你需要的是任何预期的命名参数的源代码和列表(令牌使用三重括号标记,如嵌入式{{{0}} },以避免与string.Format()和类似Handlebars的语法发生冲突:

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库。它提供了动态获取/设置/调用成员(包括嵌套成员)的方法,或者如果明确定义了成员,则可以创建比反射更快的getter / setter(lambda编译为委托):

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

或者,如果属性的数量不是无穷无尽的,则可以生成setter并对其进行排序(由于它们是编译的委托,因此setter更快):

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

正确的答案是您需要缓存所有结果以保持较低的内存使用率。

一个例子看起来像这样

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预编译所有可能的结果... 而且没有副作用/外部输入...是啊,这个想法在理论上作品。但是,该代码是一个很大的语法错误
2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.