Как я могу оценить код C # динамически? - PullRequest
84 голосов
/ 07 августа 2008

Я могу сделать eval("something()"); для динамического выполнения кода в JavaScript. Есть ли способ для меня сделать то же самое в C #?

Пример того, что я пытаюсь сделать: у меня есть целочисленная переменная (скажем, i), и у меня есть несколько свойств по именам: "Property1", "Property2", "Property3" и т. Д. Теперь я хочу выполнить некоторые операции со свойством «Свойство i » в зависимости от значения i.

Это действительно просто с Javascript. Есть ли способ сделать это с C #?

Ответы [ 16 ]

49 голосов
/ 07 августа 2008

К сожалению, 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 Метод

20 голосов
/ 23 июня 2016

Использование скриптового API Roslyn (больше примеров здесь ):

// 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 голосов
/ 07 августа 2008

Не совсем. Вы можете использовать рефлексию, чтобы достичь того, чего хотите, но это будет не так просто, как в 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);
}
9 голосов
/ 03 февраля 2013

Я написал проект с открытым исходным кодом, Dynamic Expresso , который может преобразовывать текстовые выражения, написанные с использованием синтаксиса C #, в делегаты (или дерево выражений). Выражения анализируются и преобразуются в Деревья выражений без использования компиляции или отражения.

Вы можете написать что-то вроде:

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.

9 голосов
/ 23 июня 2011

Это функция eval в c #. Я использовал его для преобразования анонимных функций (лямбда-выражений) из строки. Источник: 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;

}
7 голосов
/ 07 августа 2008

Все это определенно будет работать. Лично для этой конкретной проблемы я бы, вероятно, выбрал немного другой подход. Может быть, что-то вроде этого:

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 голосов
/ 14 октября 2009

Сейчас нет, если вы абсолютно хотите выполнять операторы C #, но вы уже можете выполнять операторы Javascript в C # 2.0. Библиотека с открытым исходным кодом Jint может это сделать. Это интерпретатор Javascript для .NET. Передайте программу Javascript, и она будет работать внутри вашего приложения. Вы даже можете передать объект C # в качестве аргументов и выполнить его автоматизацию.

Также, если вы просто хотите оценить выражение ваших свойств, попробуйте NCalc .

4 голосов
/ 07 августа 2008

Вы можете использовать отражение, чтобы получить свойство и вызвать его. Примерно так:

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

То есть, если объект, обладающий свойством, называется "theObject":)

2 голосов
/ 01 октября 2010

Вы также можете реализовать веб-браузер, а затем загрузить html-файл, содержащий javascript.

Затем перейдите к методу document.InvokeScript в этом браузере. Возвращаемое значение функции eval может быть перехвачено и преобразовано во все, что вам нужно.

Я сделал это в нескольких проектах, и он отлично работает.

Надеюсь, это поможет

0 голосов
/ 13 июля 2018

Вы можете проверить библиотеку Heleonix.Reflection . Он предоставляет методы для динамического получения / установки / вызова членов, включая вложенные элементы, или, если член четко определен, вы можете создать метод получения / установки (лямбда-код, скомпилированный в делегат), который быстрее, чем отражение:

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

Или, если число свойств не является бесконечным, вы можете генерировать сеттеры и выбирать их (сеттеры работают быстрее, поскольку они являются скомпилированными делегатами):

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

Сеттеры могут иметь тип Action<object, object>, но экземпляры могут отличаться во время выполнения, поэтому вы можете создавать списки сеттеров.

...