Bagaimana saya dapat mengevaluasi kode C # secara dinamis?


93

Saya bisa melakukan eval("something()");untuk mengeksekusi kode secara dinamis di JavaScript. Apakah ada cara bagi saya untuk melakukan hal yang sama di C #?

Contoh dari apa yang saya coba lakukan adalah: Saya memiliki variabel integer (katakanlah i) dan saya memiliki beberapa properti dengan nama: "Property1", "Property2", "Property3", dll. Sekarang, saya ingin melakukan beberapa operasi pada properti "Properti i " tergantung pada nilai i.

Ini sangat sederhana dengan Javascript. Apakah ada cara untuk melakukan ini dengan C #?



2
c # panggil eval dari ironpython. Saya mencobanya di c # 4.0. tidak ada pengalaman dengan c # 2.0
Peter Long

@Peter Long, di mana saya dapat menemukan dokumentasi tentang eval IronPython?
smartcaveman

@AdhipGupta Saya tahu T&J ini cukup kuno, tetapi saya baru saja merilis daftar putar video yang terdengar sangat mirip dengan deskripsi yang diberikan dalam jawaban Davide Icardi. Ini sangat berbeda, dan mungkin layak untuk dicoba.
Rick Riggs

Jawaban:


49

Sayangnya, C # bukanlah bahasa dinamis seperti itu.

Apa yang dapat Anda lakukan, bagaimanapun, adalah membuat file kode sumber C #, penuh dengan kelas dan semuanya, dan menjalankannya melalui penyedia CodeDom untuk C # dan mengkompilasinya menjadi sebuah rakitan, dan kemudian mengeksekusinya.

Posting forum di MSDN ini berisi jawaban dengan beberapa contoh kode di bawah halaman:
buat metode anonim dari string?

Saya tidak akan mengatakan ini adalah solusi yang sangat baik, tetapi tetap mungkin.

Jenis kode apa yang akan Anda harapkan dalam string itu? Jika itu adalah bagian kecil dari kode yang valid, misalnya hanya ekspresi matematika, mungkin ada alternatif lain.


Sunting : Nah, itu mengajari saya untuk membaca pertanyaan dengan seksama terlebih dahulu. Ya, refleksi akan bisa membantu Anda di sini.

Jika Anda membagi string dengan; pertama, untuk mendapatkan properti individu, Anda dapat menggunakan kode berikut untuk mendapatkan objek PropertyInfo untuk properti tertentu untuk kelas, dan kemudian menggunakan objek tersebut untuk memanipulasi objek tertentu.

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

Tautan: Metode PropertyInfo.SetValue


bagaimana jika GetProperty harus berupa fungsi dengan x, y params?, artinya Teks (1,2)?
pengguna1735921

@ user1735921 Kemudian Anda perlu menggunakan GetMethod(methodName), mengurai nilai parameter, dan memanggil metode menggunakan refleksi.
Lasse V. Karlsen

typeof (ObjectType) analog dengan someObject.GetType ()
Thiago

35

Menggunakan Roslyn scripting API (lebih banyak contoh di sini ):

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

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

Anda juga dapat menjalankan kode apa pun:

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

Dan mereferensikan kode yang dibuat di proses sebelumnya:

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

14

Tidak juga. Anda dapat menggunakan refleksi untuk mencapai apa yang Anda inginkan, tetapi itu tidak akan sesederhana di Javascript. Misalnya, jika Anda ingin menyetel bidang pribadi suatu objek menjadi sesuatu, Anda dapat menggunakan fungsi ini:

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

Ini adalah fungsi eval di bawah c #. Saya menggunakannya untuk mengubah fungsi anonim (Ekspresi Lambda) dari sebuah string. Sumber: 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 Whoops, saya mengoreksi kesalahan ketik (Lambada => Lambda). Saya tidak tahu bahwa lagu itu berjudul Lambada jadi yang ini tidak disengaja. ;)
Largo

Saya tidak mengerti mengapa jawaban ini mendapatkan lebih sedikit suara. Ini sangat berguna.
Muzaffer Galata

9

Saya telah menulis proyek open source, Dynamic Expresso , yang dapat mengubah ekspresi teks yang ditulis menggunakan sintaks C # menjadi delegasi (atau pohon ekspresi). Ekspresi diurai dan diubah menjadi Pohon Ekspresi tanpa menggunakan kompilasi atau refleksi.

Anda bisa menulis sesuatu seperti:

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

atau

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

Pekerjaan saya didasarkan pada artikel Scott Gu http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx .


7

Semua itu pasti akan berhasil. Secara pribadi, untuk masalah khusus itu, saya mungkin akan mengambil pendekatan yang sedikit berbeda. Mungkin sesuatu seperti ini:

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

Saat menggunakan pola seperti ini, Anda harus berhati-hati karena data Anda disimpan dengan referensi dan bukan dengan nilai. Dengan kata lain, jangan lakukan ini dengan orang primitif. Anda harus menggunakan rekan kelas mereka yang besar.

Saya menyadari bukan itu pertanyaannya, tetapi pertanyaannya telah dijawab dengan cukup baik dan saya pikir mungkin pendekatan alternatif dapat membantu.


5

Saya tidak sekarang jika Anda benar-benar ingin menjalankan pernyataan C #, tetapi Anda sudah dapat menjalankan pernyataan Javascript di C # 2.0. Pustaka sumber terbuka Jint mampu melakukannya. Ini adalah juru bahasa Javascript untuk .NET. Lulus program Javascript dan itu akan berjalan di dalam aplikasi Anda. Anda bahkan dapat mengirimkan objek C # sebagai argumen dan melakukan otomasi padanya.

Juga jika Anda hanya ingin mengevaluasi ekspresi pada properti Anda, cobalah NCalc .


3

Anda dapat menggunakan refleksi untuk mendapatkan properti dan memintanya. Sesuatu seperti ini:

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

Artinya, dengan asumsi objek yang memiliki properti disebut "theObject" :)


2

Anda juga bisa mengimplementasikan Webbrowser, lalu memuat file html yang berisi javascript.

Kemudian Anda pergi untuk document.InvokeScriptMetode di browser ini. Nilai kembali dari fungsi eval dapat ditangkap dan diubah menjadi semua yang Anda butuhkan.

Saya melakukan ini di beberapa Proyek dan bekerja dengan sempurna.

Semoga membantu


0

Anda dapat melakukannya dengan fungsi prototipe:

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

dan seterusnya...



0

Saya telah menulis sebuah paket, SharpByte.Dynamic , untuk menyederhanakan tugas menyusun dan menjalankan kode secara dinamis. Kode dapat dipanggil pada objek konteks apa pun menggunakan metode ekstensi seperti yang dijelaskan lebih lanjut di sini .

Sebagai contoh,

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

mengembalikan 3;

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

mengembalikan representasi string objek konteks;

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

menjalankan pernyataan tersebut sebagai skrip, dll.

Executable bisa didapatkan dengan mudah menggunakan metode pabrik, seperti yang terlihat pada contoh di sini - yang Anda butuhkan hanyalah kode sumber dan daftar parameter bernama yang diharapkan (token disematkan menggunakan notasi braket tiga, seperti {{{0}} }, untuk menghindari tabrakan dengan string.Format () serta sintaks mirip Handlebars):

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

Setiap objek yang dapat dieksekusi (skrip atau ekspresi) aman untuk thread, dapat disimpan dan digunakan kembali, mendukung logging dari dalam skrip, menyimpan informasi waktu dan pengecualian terakhir jika ditemukan, dll. Ada juga metode Copy () yang dikompilasi pada masing-masing untuk memungkinkan membuat salinan murah, yaitu menggunakan objek yang dapat dieksekusi yang dikompilasi dari skrip atau ekspresi sebagai template untuk membuat yang lain.

Overhead dari mengeksekusi skrip atau pernyataan yang sudah dikompilasi relatif rendah, di bawah mikrodetik pada perangkat keras sederhana, dan skrip dan ekspresi yang sudah dikompilasi di-cache untuk digunakan kembali.


0

Saya mencoba untuk mendapatkan nilai dari struktur (kelas) anggota dengan namanya. Strukturnya tidak dinamis. Semua jawaban tidak berhasil sampai saya akhirnya mendapatkannya:

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

Metode ini akan mengembalikan nilai anggota dengan namanya. Ia bekerja pada struktur biasa (kelas).


0

Anda dapat memeriksa perpustakaan Heleonix.Reflection . Ini menyediakan metode untuk mendapatkan / menyetel / memanggil anggota secara dinamis, termasuk anggota bertingkat, atau jika anggota didefinisikan dengan jelas, Anda dapat membuat pengambil / penyetel (lambda dikompilasi menjadi delegasi) yang lebih cepat daripada refleksi:

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

Atau jika jumlah properti tidak terbatas, Anda dapat membuat setter dan menyimpannya di cache (setter lebih cepat karena mereka adalah delegasi yang dikompilasi):

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

Setter bisa berjenis Action<object, object>tetapi instance bisa berbeda pada waktu proses, jadi Anda bisa membuat daftar setter.


-1

Sayangnya, C # tidak memiliki fasilitas asli untuk melakukan apa yang Anda minta.

Namun, program evaluasi C # saya memungkinkan untuk mengevaluasi kode C #. Ini menyediakan untuk mengevaluasi kode C # saat runtime dan mendukung banyak pernyataan C #. Faktanya, kode ini dapat digunakan dalam proyek .NET apa pun, namun, ini terbatas untuk menggunakan sintaks C #. Kunjungi situs web saya, http://csharp-eval.com , untuk detail tambahan.



-9

jawaban yang benar adalah Anda perlu men-cache semua hasil untuk menjaga penggunaan mem0ry tetap rendah.

sebuah contoh akan terlihat seperti ini

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

dan menambahkannya ke Daftar

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

simpan id dan gunakan dalam kode

semoga ini membantu


5
seseorang bingung mengevaluasi dengan pencarian. Jika Anda mengetahui semua kemungkinan program (saya pikir itu setidaknya NP-Hard) ... dan Anda memiliki mesin super untuk mengkompilasi semua hasil yang mungkin ... dan tidak ada efek samping / input eksternal ... Ya, ide ini secara teoritis berfungsi . Kode tersebut adalah salah satu kesalahan sintaksis yang besar
lihat
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.