Вопрос

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

Пример того, что я пытаюсь сделать:У меня есть целочисленная переменная (скажем i), и у меня есть несколько свойств по именам:«Собственность1», «Собственность2», «Собственность3» и т. д.Теперь я хочу выполнить некоторые операции над свойствомя «имущество в зависимости от стоимости i.

С Javascript это действительно просто.Есть ли способ сделать это с помощью С#?

Это было полезно?

Решение

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

Другие советы

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

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

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

}

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

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

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#, но вы уже можете выполнять операторы Javascript в C# 2.0.Библиотека с открытым исходным кодом Джинт способен это сделать.Это интерпретатор Javascript для .NET.Передайте программу Javascript, и она будет работать внутри вашего приложения.Вы даже можете передать объект C# в качестве аргументов и выполнить на нем автоматизацию.

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

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

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

То есть при условии, что объект, обладающий этим свойством, называется «theObject» :)

Вы также можете реализовать веб-браузер, а затем загрузить 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.Dynamic, чтобы упростить задачу динамической компиляции и выполнения кода.Код можно вызвать для любого объекта контекста, используя методы расширения, как описано далее. здесь.

Например,

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}}}, чтобы избежать конфликтов со string.Format(), а также с Handlebars- как синтаксис):

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

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

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

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

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

пример будет выглядеть так

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top