Сведения о вопросе

GANGST1ER

16:03, 1st July, 2020

Теги

c#   reflection   properties   c#-2.0    

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

Просмотров: 511   Ответов: 16

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

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

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



  Сведения об ответе

ЯЯ__4

18:03, 1st July, 2020

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

Однако вы можете создать файл исходного кода C#, полный класса и всего остального, и запустить его через провайдер CodeDom для C#, скомпилировать его в assembly, а затем выполнить его.

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

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

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


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

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

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

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


  Сведения об ответе

PAGE

18:03, 1st July, 2020

Использование сценария Roslyn API (дополнительные примеры здесь ):

// 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();");


  Сведения об ответе

fo_I_K

18:03, 1st July, 2020

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


  Сведения об ответе

FAriza

18:03, 1st July, 2020

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

}


  Сведения об ответе

piter

18:03, 1st July, 2020

Я написал проект с открытым исходным кодом 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 .


  Сведения об ответе

DAAA

18:03, 1st July, 2020

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

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

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

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


  Сведения об ответе

darknet

18:03, 1st July, 2020

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

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


  Сведения об ответе

qwerty101

18:03, 1st July, 2020

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

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

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


  Сведения об ответе

ITSME

18:03, 1st July, 2020

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

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

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

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


  Сведения об ответе

baggs

18:03, 1st July, 2020

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

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

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


  Сведения об ответе

#hash

18:03, 1st July, 2020

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

Способ DataBinder.Eval


  Сведения об ответе

FAriza

18:03, 1st July, 2020

Я написал пакет 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(), а также синтаксисов, подобных рулям):

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

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

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


  Сведения об ответе

прога

18:03, 1st July, 2020

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

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


  Сведения об ответе

JUST___

18:03, 1st July, 2020

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

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

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


  Сведения об ответе

SILA

18:03, 1st July, 2020

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

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


  Сведения об ответе

lourence

18:03, 1st July, 2020

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

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться