C # kodunu dinamik olarak nasıl değerlendirebilirim?


94

eval("something()");JavaScript'te kodu dinamik olarak çalıştırmak için bir yapabilirim . Aynı şeyi C # 'da yapmamın bir yolu var mı?

Yapmaya çalıştığım şeye bir örnek: Bir tamsayı değişkenim var (diyelim i) ve birden çok özelliğim var: "Özellik1", "Özellik2", "Özellik3", vb. Şimdi, bazı işlemleri gerçekleştirmek istiyorum. değerine bağlı olarak "Özellik i " özelliğinde i.

Javascript ile bu gerçekten çok basit. Bunu C # ile yapmanın bir yolu var mı?



3
c # ironpython'ın eval. Bunu c # 4.0'da denedim. c # 2.0 ile deneyim yok
Peter Long

@Peter Long, IronPython'un eval ile ilgili belgeleri nerede bulabilirim?
smartcaveman

@AdhipGupta Bu Soru-Cevap'ın oldukça tarihli olduğunu biliyorum, ancak Davide Icardi'nin cevabında verilen açıklamaya çok benzeyen bir video oynatma listesi yayınladım . Önemli ölçüde farklıdır ve muhtemelen incelemeye değer.
Rick Riggs

Yanıtlar:


49

Ne yazık ki, C # böyle dinamik bir dil değil.

Bununla birlikte, yapabileceğiniz şey, sınıfla ve her şeyle dolu bir C # kaynak kodu dosyası oluşturmak ve bunu C # için CodeDom sağlayıcısı aracılığıyla çalıştırmak ve bunu bir derlemede derlemek ve ardından yürütmektir.

MSDN üzerindeki bu forum gönderisi, bir şekilde sayfanın altındaki bazı örnek kodlarla birlikte bir cevap içerir:
bir dizeden anonim bir yöntem oluşturmak?

Bunun çok iyi bir çözüm olduğunu pek söyleyemem ama yine de mümkün.

Bu dizede ne tür bir kod bekleyeceksiniz? Geçerli kodun küçük bir alt kümesiyse, örneğin yalnızca matematik ifadeleriyse, başka alternatifler de olabilir.


Düzenleme : Bu bana önce soruları iyice okumayı öğretir. Evet, yansıma burada size biraz yardımcı olabilir.

Dizeyi; ilk olarak, bireysel özellikleri elde etmek için, bir sınıfa ilişkin belirli bir özellik için bir PropertyInfo nesnesi almak üzere aşağıdaki kodu kullanabilir ve ardından belirli bir nesneyi işlemek için bu nesneyi kullanabilirsiniz.

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

Bağlantı: PropertyInfo.SetValue Yöntemi


Ya GetProperty'nin x, y parametreli bir fonksiyon olması gerekiyorsa, Metin (1,2) anlamına gelir?
user1735921

@ user1735921 Daha sonra GetMethod(methodName), bunun yerine kullanmanız , parametre değerlerini ayrıştırmanız ve yansımayı kullanarak yöntemi çağırmanız gerekecektir .
Lasse V.Karlsen

typeof (ObjectType), someObject.GetType () ile analogdur
Thiago

37

Roslyn komut dosyası API'sini kullanarak ( burada daha fazla örnek ):

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

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

Ayrıca herhangi bir kod parçasını da çalıştırabilirsiniz:

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

Ve önceki çalıştırmalarda oluşturulan koda referans verin:

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

14

Pek sayılmaz. İstediğinizi elde etmek için yansımayı kullanabilirsiniz, ancak bu neredeyse Javascript'teki kadar basit olmayacaktır. Örneğin, bir nesnenin özel alanını bir şeye ayarlamak istiyorsanız, bu işlevi kullanabilirsiniz:

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

Bu, c # altında bir eval işlevidir. Bir dizeden anonim işlevleri (Lambda İfadeleri) dönüştürmek için kullandım. Kaynak: 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, yazım hatasını düzelttim (Lambada => Lambda). Şarkının adının Lambada olduğunu bilmiyordum, bu yüzden bu kasıtsızdı. ;)
Largo

Bu cevabın neden daha az oy aldığını anlayamadım. Bu çok kullanışlı.
Muzaffer Galata

9

C # sözdizimi kullanılarak yazılmış metin ifadesini temsilcilere (veya ifade ağacına) dönüştürebilen açık kaynaklı bir proje, Dynamic Expresso yazdım. İfadeler, derleme veya yansıtma kullanmadan ayrıştırılır ve İfade Ağaçlarına dönüştürülür .

Şöyle bir şey yazabilirsiniz:

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

veya

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

Çalışmam Scott Gu'nun http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx makalesine dayanıyor .


7

Bunların hepsi kesinlikle işe yarar. Şahsen, bu özel sorun için muhtemelen biraz farklı bir yaklaşım izlerdim. Belki bunun gibi bir şey:

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

Bunun gibi kalıpları kullanırken, verilerinizin değere göre değil referansa göre saklanmasına dikkat etmelisiniz. Başka bir deyişle, bunu ilkellerle yapmayın. Onların büyük şişirilmiş sınıf muadillerini kullanmak zorundasın.

Bunun tam olarak soru olmadığını fark ettim, ancak soru oldukça iyi yanıtlandı ve alternatif bir yaklaşımın yardımcı olabileceğini düşündüm.


5

C # ifadelerini kesinlikle yürütmek istiyorsanız şimdi istemiyorum, ancak C # 2.0'da Javascript ifadelerini zaten çalıştırabilirsiniz. Açık kaynak kütüphanesi Jint bunu yapabilir. NET için bir Javascript yorumlayıcısıdır. Bir Javascript programını iletin ve uygulamanızın içinde çalışacaktır. Hatta C # nesnesini bağımsız değişken olarak iletebilir ve üzerinde otomasyon yapabilirsiniz.

Ayrıca mülklerinizin ifadesini değerlendirmek istiyorsanız, NCalc'ı deneyin .


4

Özelliği almak ve çağırmak için yansımayı kullanabilirsiniz. Bunun gibi bir şey:

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

Yani, özelliğe sahip nesnenin "theObject" olarak adlandırıldığını varsayarsak :)


2

Ayrıca bir Web tarayıcısı uygulayabilir, ardından javascript içeren bir html dosyası yükleyebilirsiniz.

Sonra document.InvokeScriptbu tarayıcıda Yönteme gidin . Eval işlevinin dönüş Değeri yakalanabilir ve ihtiyacınız olan her şeye dönüştürülebilir.

Bunu birkaç projede yaptım ve mükemmel çalışıyor.

Umarım yardımcı olur


0

Bunu bir prototip işlevi ile yapabilirsiniz:

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

ve bunun gibi...



0

Kodu dinamik olarak derleme ve çalıştırma görevini basitleştirmek için SharpByte.Dynamic adlı bir paket yazdım . Kod, burada daha ayrıntılı olarak açıklandığı üzere uzantı yöntemleri kullanılarak herhangi bir bağlam nesnesinde çağrılabilir .

Örneğin,

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

3 döndürür;

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

bağlam nesnesinin dizgi temsilini döndürür;

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

bu ifadeleri komut dosyası vb. olarak çalıştırır.

Örnekte görüldüğü gibi Çalıştırılabilirler, bir fabrika yöntemini kullanarak kolayca kazanılmış olabilir burada size gereken herhangi beklenen adlandırılmış parametrelerin kaynak kodu ve liste (jetonlar gibi üçlü köşeli parantez açıklaması kullanarak gömülü olduğu --all {{{0}} }, string.Format () ve Gidon benzeri sözdizimleriyle çakışmaları önlemek için):

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

Her yürütülebilir nesne (komut dosyası veya ifade) iş parçacığı açısından güvenlidir, depolanabilir ve yeniden kullanılabilir, bir komut dosyası içinden günlüğe kaydetmeyi destekler, zamanlama bilgilerini depolar ve karşılaşılırsa son istisnayı vb. ucuz kopyalar yaratmak, yani bir komut dosyası veya ifadeden derlenen çalıştırılabilir bir nesneyi başkalarını oluşturmak için şablon olarak kullanmak.

Önceden derlenmiş bir komut dosyası veya ifadeyi yürütmenin ek yükü, nispeten düşüktür, mütevazı donanımda bir mikrosaniyenin çok altında ve önceden derlenmiş komut dosyaları ve ifadeler yeniden kullanım için önbelleğe alınır.


0

İsmine göre bir yapı (sınıf) üyesinin değerini almaya çalışıyordum. Yapı dinamik değildi. Sonunda onu alana kadar tüm cevaplar işe yaramadı:

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

Bu yöntem üyenin değerini adına göre döndürecektir. Normal yapı (sınıf) üzerinde çalışır.


0

Sen kontrol edebilirsiniz Heleonix.Reflection kütüphanesi. İç içe geçmiş üyeler de dahil olmak üzere üyeleri dinamik olarak almak / ayarlamak / çağırmak için yöntemler sağlar veya bir üye açıkça tanımlanmışsa, yansımadan daha hızlı olan bir alıcı / ayarlayıcı (bir temsilciye derlenmiş lambda) oluşturabilirsiniz:

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

Veya özelliklerin sayısı sonsuz değilse, ayarlayıcılar oluşturabilir ve onları chache edebilirsiniz (ayarlayıcılar, temsilciler derlendikleri için daha hızlıdır):

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

Ayarlayıcılar türde olabilir, Action<object, object>ancak örnekler çalışma zamanında farklı olabilir, böylece ayarlayıcı listeleri oluşturabilirsiniz.


-1

Ne yazık ki, C #, tam olarak istediğiniz şeyi yapmak için herhangi bir yerel olanaklara sahip değil.

Ancak, C # eval programım C # kodunu değerlendirmeye izin veriyor. Çalışma zamanında C # kodunun değerlendirilmesini sağlar ve birçok C # ifadesini destekler. Aslında, bu kod herhangi bir .NET projesinde kullanılabilir, ancak C # sözdizimini kullanmakla sınırlıdır. Ek ayrıntılar için web siteme, http://csharp-eval.com bir göz atın .



-9

doğru cevap, mem0ry kullanımını düşük tutmak için tüm sonuçları önbelleğe almanız gerektiğidir.

bir örnek şuna benzer

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

ve bir Listeye ekleyin

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

kimliği kaydedin ve kodda kullanın

Bu yardımcı olur umarım


5
birisi değerlendirmeyi arama ile karıştırdı. Tüm olası programları biliyorsanız ( bence bu en azından NP-Zor) ... ve tüm olası sonuçları önceden derlemek için bir süper makineniz varsa ... ve yan etkiler / harici girdiler yoksa ... Evet, bu fikir teorik olarak işe yarıyor . Kod büyük bir sözdizimi hatasıdır
sehe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.