2008-08-07 110 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和重複使用,支持從腳本內登錄,存儲定時信息和最後異常,如果遇到等也有複印編譯上的每個,以允許創建廉價複製,即,使用從腳本或表達式編譯的可執行對象()方法作爲創建他人的模板。

執行的已編譯腳本或語句的開銷比較低,遠低於一般硬體上一微秒,並且已經編譯腳本和表達式緩存重用。