2008-08-07 109 views
73

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

我正在尝试做的是,我有一个整型变量(说i),我有名称的多个属性:“Property1”,“Property2”,“Property3”等。 现在,我想要根据i的值对“属性i”属性执行一些操作。

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

+2

C#调用IronPython中的eval。我在c#4.0中试过。用C#2.0 – 2011-05-15 03:00:56

+0

@Peter龙没有经验,我在哪里可以找到文档上的IronPython的eval? – smartcaveman 2011-06-16 16:09:41

+0

看看[Mono的CSHARP交互的shell(http://www.mono-project.com/CsharpRepl)。它具有[eval-like functions](http://www.go-mono.com/docs/index.aspx?link=N:Mono.CSharp)。 – 2011-02-27 20:28:24

回答

40

不幸的是,C#是不是这样的动态语言。然而,你可以做的是创建一个C#源代码文件,该文件充满了类和所有内容,并通过CodeDom提供程序运行C#并将其编译为程序集,然后执行它。

这在MSDN论坛帖子包含一些示例代码下来有些页面的答案:
create a anonymous method from a string?

我很难说这是一个很好的解决方案,但它是可能无妨。

你会期待什么样的代码在字符串中?如果它是有效代码的次要子集,例如只是数学表达式,则可能存在其他替代方案。


编辑:好了,教我彻底先读问题。是的,反思能够在这里给你一些帮助。

如果你用字符串分割字符串;首先,要获取单个属性,可以使用以下代码为某个类的特定属性获取PropertyInfo对象,然后使用该对象来操作特定对象。

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

链接:PropertyInfo.SetValue Method

2

您可以使用反射来获取属性并调用它。事情是这样的:

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

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

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

等上...

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

1

你也可以实现一个Webbrowser,然后加载一个包含javascript的html文件。

然后你去这个浏览器的document.InvokeScript方法。 eval函数的返回值可以被捕获并转换为您需要的所有内容。

我在几个项目中做了这个,它的功能完美。

希望它可以帮助

-1

不幸的是,C#不具备做的正是你所要求的任何本地设施。

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

+0

看一看[罗斯林脚本API](https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples) – 2017-10-22 16:23:42

7

这是一个在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; 

} 
+0

@sehe糟糕,我纠正了错字(伦巴达=> LAMBDA)。我不知道这首歌叫做Lambada,所以这是一个无意的故事。 ;) – Largo 2011-12-23 22:47:03

-7

正确的答案是,你需要缓存所有的结果保持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,并在代码中使用它

希望这有助于

7

我写了一个开源项目,Dynamic Expresso,可转换使用C#语法为代表(或表达式树)的书面文字表达。表达式被解析并转换成Expression Trees而不使用编译或反射。

您可以编写类似:

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

6

使用罗斯林脚本API(更多samples here):

// 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();"); 
0

我写了一个包,SharpByte.Dynamic,以简化任务o f动态编译和执行代码。可以使用扩展方法在任何上下文对象上调用该代码,详见here

例如,

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

运行这些语句作为脚本等

可执行文件可以很容易地利用工厂方法得到,如示例here --all你需要看到的是任何预期的源代码和列表

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

每个可执行的目标:命名参数(令牌使用三重括号标记,如{{{0}}},以避免与的String.format)碰撞(以及把手状语法嵌入的) (脚本或表达式)是线程安全的,可以存储d和重复使用,支持从脚本内登录,存储定时信息和最后异常,如果遇到等也有复印编译上的每个,以允许创建廉价复制,即,使用从脚本或表达式编译的可执行对象()方法作为创建他人的模板。

执行的已编译脚本或语句的开销比较低,远低于一般硬体上一微秒,并且已经编译脚本和表达式缓存重用。