Как я могу динамически оценивать код C#?
Я могу сделать eval("something()"); для динамического выполнения кода в JavaScript. Есть ли способ для меня сделать то же самое в C#?
пример того, что я пытаюсь сделать: у меня есть целочисленная переменная (скажем i) и у меня есть несколько свойств по именам: "Property1", "Property2", "Property3" и т. д.
Теперь я хочу выполнить некоторые операции над " свойствомЯ " свойства в зависимости от значения i.
Это очень просто с Javascript. Есть ли способ сделать это с помощью C#?
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); }и так далее...
Я написал пакет, 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