ฉันจะประเมินรหัส C # แบบไดนามิกได้อย่างไร


93

ฉันสามารถeval("something()");ดำเนินการโค้ดแบบไดนามิกใน JavaScript ได้ มีวิธีให้ฉันทำสิ่งเดียวกันใน C # หรือไม่?

ตัวอย่างของสิ่งที่ฉันพยายามทำคือ: ฉันมีตัวแปรจำนวนเต็ม (พูดi) และฉันมีคุณสมบัติหลายอย่างตามชื่อ: "Property1", "Property2", "Property3" ฯลฯ ตอนนี้ฉันต้องการดำเนินการบางอย่าง ใน "ทรัพย์สินฉัน " iทรัพย์สินขึ้นอยู่กับมูลค่าของ

นี่เป็นเรื่องง่ายมากด้วย Javascript มีวิธีใดบ้างที่จะทำเช่นนี้กับ C #?


ลองดูที่เปลือกโต้ตอบของโมโน CSharp มีฟังก์ชันเหมือนการประเมิน
Igor Brejc

2
c # เรียกว่า eval ของ ironpython ฉันลองแล้วใน c # 4.0 ไม่มีประสบการณ์กับ c # 2.0
Peter Long

@Peter Long ฉันจะหาเอกสารเกี่ยวกับ eval ของ IronPython ได้ที่ไหน
smartcaveman

@AdhipGupta ฉันรู้ว่าคำถาม & คำตอบนี้ค่อนข้างล้าสมัย แต่ฉันเพิ่งเปิดตัวเพลย์ลิสต์วิดีโอที่ฟังดูคล้ายกับคำอธิบายที่ให้ไว้ในคำตอบของ Davide Icardi มันแตกต่างกันอย่างมากและน่าจะคุ้มค่าที่จะลองดู
Rick Riggs

คำตอบ:


49

น่าเสียดายที่ C # ไม่ใช่ภาษาแบบไดนามิกเช่นนั้น

อย่างไรก็ตามสิ่งที่คุณสามารถทำได้คือการสร้างไฟล์ซอร์สโค้ด C # เต็มไปด้วยคลาสและทุกอย่างและเรียกใช้ผ่านผู้ให้บริการ CodeDom สำหรับ C # และรวบรวมลงในแอสเซมบลีจากนั้นดำเนินการ

โพสต์ในฟอรัมนี้บน 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

typeof (ObjectType) เป็นแบบอะนาล็อกกับ someObject.GetType ()
Thiago

35

การใช้ Roslyn scripting 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

นี่คือฟังก์ชัน eval ภายใต้ c # ฉันใช้มันเพื่อแปลงฟังก์ชันที่ไม่ระบุตัวตน (Lambda Expressions) จากสตริง ที่มา: 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

9

ฉันได้เขียนโครงการโอเพ่นซอร์ส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);

งานของฉันจะขึ้นอยู่กับสกอตต์บทความ 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
  }
}

เมื่อใช้รูปแบบเช่นนี้คุณต้องระวังว่าข้อมูลของคุณถูกจัดเก็บโดยการอ้างอิงไม่ใช่ตามมูลค่า กล่าวอีกนัยหนึ่งคืออย่าทำสิ่งนี้กับสิ่งดั้งเดิม คุณต้องใช้คู่หูชั้นป่องตัวใหญ่ของพวกเขา

ฉันรู้ว่านั่นไม่ใช่คำถาม แต่คำถามนั้นตอบได้ค่อนข้างดีและฉันคิดว่าวิธีอื่นอาจช่วยได้


5

ตอนนี้ฉันไม่ทำถ้าคุณต้องการเรียกใช้คำสั่ง C # แต่คุณสามารถเรียกใช้คำสั่ง Javascript ใน C # 2.0 ได้แล้ว Jintไลบรารีโอเพนซอร์สสามารถทำได้ เป็นล่าม Javascript สำหรับ. NET ผ่านโปรแกรม Javascript และจะทำงานในแอปพลิเคชันของคุณ คุณยังสามารถส่งผ่านวัตถุ C # เป็นอาร์กิวเมนต์และดำเนินการอัตโนมัติได้

นอกจากนี้ถ้าคุณเพียงต้องการที่จะประเมินการแสดงออกเกี่ยวกับคุณสมบัติของคุณให้ลองไปNCalc


3

คุณสามารถใช้การสะท้อนเพื่อรับคุณสมบัติและเรียกใช้ สิ่งนี้:

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

นั่นคือสมมติว่าวัตถุที่มีคุณสมบัติเรียกว่า "theObject" :)


2

คุณยังสามารถใช้เว็บเบราว์เซอร์จากนั้นโหลดไฟล์ html ที่มี javascript

จากนั้นไปที่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 () เช่นเดียวกับไวยากรณ์ที่คล้ายกับ Handlebars):

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

อ็อบเจ็กต์ที่เรียกใช้งานได้ (สคริปต์หรือนิพจน์) แต่ละตัวเป็นเธรดที่ปลอดภัยสามารถจัดเก็บและใช้ซ้ำได้รองรับการบันทึกจากภายในสคริปต์เก็บข้อมูลเวลาและข้อยกเว้นสุดท้ายหากพบเป็นต้นนอกจากนี้ยังมีวิธีคัดลอก () ที่คอมไพล์ในแต่ละรายการเพื่อให้ การสร้างสำเนาราคาถูกกล่าวคือการใช้อ็อบเจ็กต์ปฏิบัติการที่คอมไพล์จากสคริปต์หรือนิพจน์เป็นเทมเพลตสำหรับสร้างอื่น ๆ

ค่าใช้จ่ายในการเรียกใช้สคริปต์หรือคำสั่งที่คอมไพล์ไว้แล้วค่อนข้างต่ำโดยใช้ฮาร์ดแวร์ที่เรียบง่ายไม่เกินหนึ่งไมโครวินาทีและสคริปต์และนิพจน์ที่คอมไพล์แล้วจะถูกแคชเพื่อนำมาใช้ใหม่


0

ฉันพยายามหาค่าของสมาชิกโครงสร้าง (คลาส) โดยใช้ชื่อมัน โครงสร้างไม่ได้เป็นแบบไดนามิก คำตอบทั้งหมดใช้ไม่ได้จนกว่าฉันจะได้รับในที่สุด:

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

วิธีนี้จะคืนค่าของสมาชิกตามชื่อมัน ทำงานบนโครงสร้างปกติ (คลาส)


0

คุณสามารถตรวจสอบไลบรารีHeleonix.Reflection มีวิธีการรับ / ตั้งค่า / เรียกใช้สมาชิกแบบไดนามิกรวมถึงสมาชิกที่ซ้อนกันหรือหากกำหนดสมาชิกไว้อย่างชัดเจนคุณสามารถสร้าง getter / setter (แลมบ์ดาคอมไพล์เป็นตัวแทน) ซึ่งเร็วกว่าการสะท้อนกลับ:

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

หรือหากจำนวนคุณสมบัติไม่สิ้นสุดคุณสามารถสร้าง setters และ chache ได้ (setters เร็วกว่าเนื่องจากรวบรวมตัวแทน):

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

ตัวตั้งค่าสามารถเป็นประเภทได้Action<object, object>แต่อินสแตนซ์อาจแตกต่างกันที่รันไทม์ดังนั้นคุณสามารถสร้างรายการตัวตั้งค่าได้


-1

น่าเสียดายที่ C # ไม่มีสิ่งอำนวยความสะดวกสำหรับการทำสิ่งที่คุณต้องการ

อย่างไรก็ตามโปรแกรม C # eval ของฉันอนุญาตให้มีการประเมินรหัส 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);

บันทึกรหัสและใช้ในรหัส

หวังว่านี่จะช่วยได้


5
มีคนสับสนกับการประเมินด้วยการค้นหา หากคุณรู้จักโปรแกรมที่เป็นไปได้ทั้งหมด (ฉันคิดว่าอย่างน้อยก็คือNP-Hard) ... และคุณมีซูเปอร์แมชชีนที่จะรวบรวมผลลัพธ์ที่เป็นไปได้ทั้งหมดไว้ล่วงหน้า ... และไม่มีผลข้างเคียง / อินพุตภายนอก ... ใช่แนวคิดนี้ใช้ได้ในทางทฤษฎี . รหัสเป็นหนึ่งในไวยากรณ์ผิดพลาดใหญ่แม้ว่า
sehe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.