Как я могу динамически оценивать код C#?



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



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



Это очень просто с Javascript. Есть ли способ сделать это с помощью C#?

676   16  

16 ответов:

к сожалению, C# не является таким динамическим языком.

это сообщение форума на MSDN содержит ответ с некоторым примером кода вниз по странице несколько:
создать анонимный метод из строки?

Я бы не сказала, что это очень хорошее решение, но это возможно в любом случае.

какой код вы собираетесь ожидать в этой строке? Если это незначительное подмножество допустимого кода, например просто математические выражения, возможно, существуют другие альтернативы.


Edit: Ну, это учит меня сначала внимательно читать вопросы. Да, отражение могло бы дать вам некоторую помощь здесь.

Если вы разделите строку на ; Во-первых, чтобы получить отдельные свойства, вы можно использовать следующий код для получения объекта PropertyInfo для определенного свойства для класса, а затем использовать этот объект для управления определенным объектом.

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

ссылки: PropertyInfo.Метод SetValue

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

использование 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();");

это функция 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;

}

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

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

при использовании такой модели, вы должны быть осторожны, что ваши данные хранятся по ссылке, а не по значению. Другими словами, Не делайте этого с примитивами. Вы должны использовать их большие раздутые аналоги класса.

Я понял, что это не совсем вопрос, но вопрос был очень хорошо ответил и я подумал, что альтернативный подход может помочь.

Я написал проект с открытым исходным кодом, Динамический Экспрессо, который может преобразовать текстовое выражение, написанное с использованием синтаксиса 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 .

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

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

вы можете использовать отражение, чтобы получить свойство и вызвать его. Что-то вроде этого:

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

то есть, предполагая, что объект, который имеет свойство называется "theObject":)

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

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

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

надеюсь, что это помогает

вы можете сделать это с помощью функции прототипа:

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

и так далее...

использует отражение для анализа и оценки выражения привязки данных к объекту во время выполнения.

DataBinder.Метод Eval

Я написал пакет, SharpByte.Динамический, чтобы упростить задачу компиляции и выполнения кода динамически. Код может быть вызван на любом объекте контекста с помощью методов расширения, как описано далее здесь.

например,

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}}}, чтобы избежать столкновения с строку.Формат (), а также синтаксисы, подобные рулям):

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

каждый исполняемый объект (скрипт или выражение) является потокобезопасным, может быть сохранен и повторно использован, поддерживает ведение журнала из сценария, хранит информацию о времени и последнее исключение, если встречается, и т. д. Существует также метод Copy (), скомпилированный на каждом из них, чтобы позволить создавать дешевые копии, т. е. использовать исполняемый объект, скомпилированный из сценария или выражения в качестве шаблона для создания других.

накладные расходы на выполнение уже скомпилированного скрипта или оператора относительно низки, на уровне менее микросекунды на скромном оборудовании, а уже скомпилированные скрипты и выражения кэшируются для повторного использования.

Я пытался получить значение члена структуры (класса) по его имени. Структура не была динамичной. Все ответы не работали, пока я, наконец, не получил его:

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

этот метод возвращает значение элемента по его названию. Он работает на регулярной структуре (классе).

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

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> но экземпляры могут быть разными во время выполнения, поэтому вы можете создавать списки установщиков.

к сожалению, C# не имеет никаких собственных средств для выполнения именно того, что вы просите.

однако моя программа c# eval позволяет оценивать код C#. Он обеспечивает оценку кода C# во время выполнения и поддерживает множество операторов C#. Фактически, этот код можно использовать в любом проекте .NET, однако он ограничен использованием синтаксиса C#. Взгляните на мой сайт,http://csharp-eval.com, для дополнительной информации.

правильный ответ заключается в том, что вам нужно кэшировать все результаты, чтобы сохранить использование 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);

сохраните идентификатор и используйте его в коде

надеюсь, что это помогает

Comments

    Ничего не найдено.