Làm cách nào để đánh giá động mã C #?


92

Tôi có thể eval("something()");thực thi mã động trong JavaScript. Có cách nào để tôi làm điều tương tự trong C # không?

Một ví dụ về những gì tôi đang cố gắng làm là: Tôi có một biến số nguyên (giả sử i) và tôi có nhiều thuộc tính có tên: "Property1", "Property2", "Property3", v.v. Bây giờ, tôi muốn thực hiện một số thao tác trên thuộc tính "Thuộc tính i " tùy thuộc vào giá trị của i.

Điều này thực sự đơn giản với Javascript. Có cách nào để làm điều này với C # không?



2
c # gọi eval của ironpython. Tôi đã thử nó trong c # 4.0. không có kinh nghiệm với c # 2.0
Peter Long

@Peter Long, tôi có thể tìm tài liệu về đánh giá của IronPython ở đâu?
minh

@AdhipGupta Tôi biết rằng phần Hỏi & Đáp này khá cũ, nhưng tôi vừa phát hành một danh sách phát video có vẻ giống như mô tả trong câu trả lời của Davide Icardi. Nó rất khác biệt, và có lẽ đáng để thử.
Rick Riggs

Câu trả lời:


49

Thật không may, C # không phải là một ngôn ngữ động như vậy.

Tuy nhiên, những gì bạn có thể làm là tạo một tệp mã nguồn C #, có đầy đủ lớp và mọi thứ, và chạy nó thông qua nhà cung cấp CodeDom cho C # và biên dịch nó thành một assembly, rồi thực thi nó.

Bài đăng trên diễn đàn này trên MSDN có chứa một câu trả lời với một số mã ví dụ dưới trang:
tạo một phương thức ẩn danh từ một chuỗi?

Tôi khó có thể nói đây là một giải pháp rất tốt, nhưng dù sao thì nó cũng có thể.

Bạn sẽ mong đợi loại mã nào trong chuỗi đó? Nếu nó là một tập hợp con nhỏ của mã hợp lệ, chẳng hạn chỉ là các biểu thức toán học, thì có thể có các lựa chọn thay thế khác.


Chỉnh sửa : Chà, điều đó dạy tôi đọc kỹ câu hỏi trước. Có, phản ánh sẽ có thể cung cấp cho bạn một số trợ giúp ở đây.

Nếu bạn chia chuỗi theo dấu; đầu tiên, để lấy các thuộc tính riêng lẻ, bạn có thể sử dụng đoạn mã sau để lấy một đối tượng PropertyInfo cho một thuộc tính cụ thể cho một lớp, sau đó sử dụng đối tượng đó để thao tác một đối tượng cụ thể.

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

Liên kết: Phương thức PropertyInfo.SetValue


điều gì sẽ xảy ra nếu GetProperty phải là một hàm với các tham số x, y ?, nghĩa là Text (1,2)?
user1735921

@ user1735921 Sau đó, bạn sẽ cần sử dụng GetMethod(methodName)thay thế, phân tích cú pháp các giá trị tham số và gọi phương thức bằng cách sử dụng phản xạ.
Lasse V. Karlsen

typeof (ObjectType) tương tự với someObject.GetType ()
Thiago

34

Sử dụng API tập lệnh Roslyn ( các mẫu khác tại đây ):

// add NuGet package 'Microsoft.CodeAnalysis.Scripting'
using Microsoft.CodeAnalysis.CSharp.Scripting;

await CSharpScript.EvaluateAsync("System.Math.Pow(2, 4)") // returns 16

Bạn cũng có thể chạy bất kỳ đoạn mã nào:

var script = await CSharpScript.RunAsync(@"
                class MyClass
                { 
                    public void Print() => System.Console.WriteLine(1);
                }")

Và tham chiếu mã đã được tạo trong các lần chạy trước:

await script.ContinueWithAsync("new MyClass().Print();");

14

Không hẳn vậy. Bạn có thể sử dụng phản xạ để đạt được những gì bạn muốn, nhưng nó sẽ không đơn giản như trong Javascript. Ví dụ: nếu bạn muốn đặt trường riêng tư của một đối tượng thành một thứ gì đó, bạn có thể sử dụng hàm này:

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

Đây là một hàm eval trong c #. Tôi đã sử dụng nó để chuyển đổi các hàm ẩn danh (Biểu thức Lambda) từ một chuỗi. Nguồn: 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 Rất tiếc, tôi đã sửa lỗi chính tả (Lambada => Lambda). Tôi không biết rằng bài hát có tên là Lambada nên đây là bài hát vô tình. ;)
Largo

Tôi không thể hiểu tại sao câu trả lời này nhận được ít phiếu bầu hơn. Nó rất hữu ích.
Muzaffer Galata

9

Tôi đã viết một dự án nguồn mở, Dynamic Expresso , có thể chuyển đổi biểu thức văn bản được viết bằng cú pháp C # thành các đại biểu (hoặc cây biểu thức). Biểu thức được phân tích cú pháp và chuyển đổi thành Cây biểu thức mà không cần sử dụng biên dịch hoặc phản chiếu.

Bạn có thể viết một cái gì đó như:

var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");

hoặc là

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

Công việc của tôi dựa trên bài viết của Scott Gu http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx .


7

Tất cả những điều đó chắc chắn sẽ hoạt động. Cá nhân, đối với vấn đề cụ thể đó, tôi có thể sẽ có một cách tiếp cận khác một chút. Có thể như thế này:

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

Khi sử dụng các mẫu như thế này, bạn phải cẩn thận rằng dữ liệu của bạn được lưu trữ theo tham chiếu chứ không phải theo giá trị. Nói cách khác, không làm điều này với các nguyên thủy. Bạn phải sử dụng các đối tác lớp lớn cồng kềnh của họ.

Tôi nhận ra đó không phải là câu hỏi chính xác, nhưng câu hỏi đã được trả lời khá tốt và tôi nghĩ có thể một cách tiếp cận thay thế có thể hữu ích.


5

Bây giờ tôi không thực hiện nếu bạn thực sự muốn thực thi các câu lệnh C #, nhưng bạn đã có thể thực thi các câu lệnh Javascript trong C # 2.0. Thư viện mã nguồn mở Jint có thể làm được điều đó. Nó là một trình thông dịch Javascript cho .NET. Chuyển một chương trình Javascript và nó sẽ chạy bên trong ứng dụng của bạn. Bạn thậm chí có thể chuyển đối tượng C # làm đối số và tự động hóa nó.

Ngoài ra, nếu bạn chỉ muốn đánh giá biểu thức trên thuộc tính của mình, hãy thử NCalc .


3

Bạn có thể sử dụng phản xạ để lấy thuộc tính và gọi nó. Một cái gì đó như thế này:

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

Đó là, giả sử đối tượng có thuộc tính được gọi là "theObject" :)


2

Bạn cũng có thể triển khai trình duyệt Web, sau đó tải một tệp html chứa javascript.

Sau đó, bạn tìm document.InvokeScriptPhương pháp trên trình duyệt này. Giá trị trả về của hàm eval có thể được bắt và chuyển đổi thành mọi thứ bạn cần.

Tôi đã làm điều này trong một số Dự án và nó hoạt động hoàn hảo.

Hy vọng nó giúp


0

Bạn có thể làm điều đó với một hàm nguyên mẫu:

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

và như thế...



0

Tôi đã viết một gói, SharpByte.Dynamic , để đơn giản hóa tác vụ biên dịch và thực thi mã động. Mã có thể được gọi trên bất kỳ đối tượng ngữ cảnh nào bằng cách sử dụng các phương thức mở rộng như được trình bày chi tiết ở đây .

Ví dụ,

someObject.Evaluate<int>("6 / {{{0}}}", 3))

trả về 3;

someObject.Evaluate("this.ToString()"))

trả về biểu diễn chuỗi của đối tượng ngữ cảnh;

someObject.Execute(@
"Console.WriteLine(""Hello, world!"");
Console.WriteLine(""This demonstrates running a simple script"");
");

chạy các câu lệnh đó như một tập lệnh, v.v.

Các tệp thực thi có thể được lấy dễ dàng bằng cách sử dụng phương thức gốc, như được thấy trong ví dụ ở đây - tất cả những gì bạn cần là mã nguồn và danh sách bất kỳ tham số được đặt tên dự kiến ​​nào (mã thông báo được nhúng bằng ký hiệu dấu ngoặc kép, chẳng hạn như {{{0}} }, để tránh xung đột với string.Format () cũng như các cú pháp giống như Handlebars):

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

Mỗi đối tượng thực thi (tập lệnh hoặc biểu thức) đều an toàn theo luồng, có thể được lưu trữ và sử dụng lại, hỗ trợ ghi nhật ký từ bên trong tập lệnh, lưu trữ thông tin thời gian và ngoại lệ cuối cùng nếu gặp phải, v.v. Ngoài ra, có một phương thức Copy () được biên dịch trên mỗi tập lệnh để cho phép tạo các bản sao rẻ tiền, tức là sử dụng một đối tượng thực thi được biên dịch từ tập lệnh hoặc biểu thức làm mẫu để tạo các đối tượng khác.

Chi phí thực thi một tập lệnh hoặc câu lệnh đã được biên dịch tương đối thấp, chỉ dưới một micro giây trên phần cứng khiêm tốn và các tập lệnh và biểu thức đã được biên dịch được lưu vào bộ nhớ đệm để sử dụng lại.


0

Tôi đang cố gắng lấy giá trị của một cấu trúc (lớp) thành viên theo tên của nó. Cấu trúc không năng động. Tất cả các câu trả lời đều không hoạt động cho đến khi tôi cuối cùng nhận được nó:

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

Phương thức này sẽ trả về giá trị của thành viên theo tên của nó. Nó hoạt động trên cấu trúc thông thường (lớp).


0

Bạn có thể kiểm tra thư viện Heleonix.Reflection . Nó cung cấp các phương thức để lấy / đặt / gọi động các thành viên, bao gồm các thành viên lồng nhau hoặc nếu một thành viên được xác định rõ ràng, bạn có thể tạo getter / setter (lambda được biên dịch thành ủy nhiệm) nhanh hơn so với phản ánh:

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

Hoặc nếu số lượng thuộc tính không phải là vô tận, bạn có thể tạo các bộ định tuyến và sắp xếp chúng (bộ định vị nhanh hơn vì chúng là các đại biểu được biên dịch):

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

Bộ định thời có thể có nhiều loại Action<object, object>nhưng các phiên bản có thể khác nhau trong thời gian chạy, vì vậy bạn có thể tạo danh sách bộ định tuyến.


-1

Thật không may, C # không có bất kỳ cơ sở gốc nào để thực hiện chính xác những gì bạn đang yêu cầu.

Tuy nhiên, chương trình eval C # của tôi không cho phép đánh giá mã C #. Nó cung cấp để đánh giá mã C # trong thời gian chạy và hỗ trợ nhiều câu lệnh C #. Trên thực tế, mã này có thể sử dụng trong bất kỳ dự án .NET nào, tuy nhiên, nó bị giới hạn trong việc sử dụng cú pháp C #. Hãy xem trang web của tôi, http://csharp-eval.com , để biết thêm chi tiết.



-9

câu trả lời đúng là bạn cần phải lưu vào bộ nhớ cache tất cả kết quả để giữ cho mức sử dụng mem0ry ở mức thấp.

một ví dụ sẽ như thế này

TypeOf(Evaluate)
{
"1+1":2;
"1+2":3;
"1+3":5;
....
"2-5":-3;
"0+0":1
} 

và thêm nó vào danh sách

List<string> results = new List<string>();
for() results.Add(result);

lưu id và sử dụng nó trong mã

hi vọng điêu nay co ich


5
ai đó nhầm lẫn đánh giá với tra cứu. Nếu bạn biết tất cả các chương trình có thể có (tôi nghĩ đó ít nhất là NP-Hard) ... và bạn có một siêu máy để biên dịch trước tất cả các kết quả có thể có ... không có tác dụng phụ / đầu vào bên ngoài ... Vâng, ý tưởng này về mặt lý thuyết hoạt động . Tuy nhiên, mã là một lỗi cú pháp lớn
xem
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.