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

我想做的一个例子是:我有一个整数变量(比如说 i)并且我有多个属性的名称:“属性 1”、“属性 2”、“属性 3”等现在,我想对“属性”执行一些操作 “财产取决于价值 i.

这对于 Javascript 来说非常简单。有没有办法用 C# 来做到这一点?

有帮助吗?

解决方案

不幸的是,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 方法

其他提示

使用 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();");

并不真地。你可以使用反射来实现你想要的,但它不会像 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);
}

这是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;

}

我写了一个开源项目, 动态浓缩咖啡, ,它可以将使用 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 .

所有这些肯定会起作用。就我个人而言,对于这个特定问题,我可能会采取一些不同的方法。也许是这样的:

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
  }
}

当使用这样的模式时,您必须小心,您的数据是按引用存储的,而不是按值存储的。换句话说,不要对基元执行此操作。你必须使用他们庞大而臃肿的类对应物。

我意识到这不完全是问题,但问题已经得到很好的回答,我认为也许另一种方法可能会有所帮助。

如果您绝对想执行 C# 语句,我现在不知道,但您已经可以在 C# 2.0 中执行 Javascript 语句。开源库 金特 是能够做到的。它是 .NET 的 Javascript 解释器。传递一个 Javascript 程序,它将在您的应用程序中运行。您甚至可以将 C# 对象作为参数传递并对其进行自动化。

另外,如果您只想评估属性的表达式,请尝试 NC计算.

您可以使用反射来获取属性并调用它。像这样的东西:

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

也就是说,假设具有该属性的对象称为“theObject”:)

您还可以实现一个 Web 浏览器,然后加载一个包含 javascript 的 html 文件。

然后你去寻找 document.InvokeScript 此浏览器上的方法。eval 函数的返回值可以被捕获并转换为您需要的任何内容。

我在几个项目中都这样做了,效果非常好。

希望能帮助到你

您可以使用原型函数来做到这一点:

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

等等...

在运行时使用反射来解析和评估对象的数据绑定表达式。

DataBinder.Eval 方法

我写了一个包, 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() 以及 Handlebars 发生冲突-像语法):

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

每个可执行对象(脚本或表达式)都是线程安全的,可以存储和重用,支持在脚本内进行日志记录,存储计时信息和最后遇到的异常等。还有一个在每个方法上编译的 Copy() 方法,以允许创建便宜的副本,即使用从脚本或表达式编译的可执行对象作为创建其他对象的模板。

执行已编译的脚本或语句的开销相对较低,在普通硬件上远低于一微秒,并且已编译的脚本和表达式会被缓存以供重用。

我试图通过名称获取结构(类)成员的值。该结构不是动态的。所有的答案都不起作用,直到我终于得到它:

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

此方法将按成员名称返回成员的值。它适用于常规结构(类)。

您可能会检查 Heleonix.反射 图书馆。它提供动态获取/设置/调用成员的方法,包括嵌套成员,或者如果明确定义了成员,则可以创建比反射更快的 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);

Setter 可以是以下类型 Action<object, object> 但实例在运行时可能会有所不同,因此您可以创建 setter 列表。

不幸的是,C# 没有任何本机工具来准确执行您所要求的操作。

但是,我的 C# eval 程序确实允许评估 C# 代码。它提供在运行时评估 C# 代码的功能,并支持许多 C# 语句。事实上,此代码可在任何 .NET 项目中使用,但是它仅限于使用 C# 语法。看看我的网站, http://csharp-eval.com, ,了解更多详细信息。

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

一个例子看起来像这样

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并在代码中使用它

希望这可以帮助

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top