Як я можу оцінити код C # динамічно?


92

Я можу зробити eval("something()");для динамічного виконання коду в JavaScript. Чи можу я зробити те саме в C #?

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

Це дуже просто з Javascript. Чи можна це зробити за допомогою C #?



2
c # виклик evalpython's eval. Я спробував це в c # 4.0. немає досвіду з c # 2.0
Пітер Лонг

@Peter Long, де я можу знайти документацію про евал IronPython?
smartcaveman

@AdhipGupta Я знаю, що ці питання та відповіді досить застарілі, але я щойно випустив відео-список відтворення, який схожий на опис, наведений у відповіді Давіде Ікарді. Це різко відрізняється, і, мабуть, варто перевірити.
Рік Ріггс,

Відповіді:


49

На жаль, 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


що, якщо GetProperty має бути функцією з параметрами x, y?, означає Text (1,2)?
user1735921

@ user1735921 Потім вам потрібно буде використовувати GetMethod(methodName)замість цього, проаналізувати значення параметрів і викликати метод за допомогою відображення.
Lasse V. Karlsen

typeof (ObjectType) є аналогом someObject.GetType ()
Тіаго,

34

Використання сценарію 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

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

11

Це функція 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;

}

1
@sehe На жаль, я виправив помилку (Lambada => Lambda). Я не знав, що пісня називається Lambada, тому ця була ненавмисною. ;)
Ларго

Я не міг зрозуміти, чому за цю відповідь менше голосів. Це дуже корисно.
Muzaffer Galata

9

Я написав проект з відкритим кодом, 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 .


7

Все це, безумовно, спрацювало б. Особисто до цієї конкретної проблеми я, мабуть, застосував би дещо інший підхід. Можливо щось подібне:

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

Зараз я не хочу, якщо ви абсолютно хочете виконати оператори C #, але ви вже можете виконувати оператори Javascript у C # 2.0. Це може зробити бібліотека з відкритим кодом Jint . Це інтерпретатор Javascript для .NET. Передайте програму Javascript, і вона запуститься всередині вашої програми. Ви навіть можете передавати об'єкт C # як аргументи та робити для нього автоматизацію.

Також якщо ви просто хочете оцінити вираз щодо своїх властивостей, спробуйте NCalc .


3

Ви можете використовувати відображення, щоб отримати властивість і викликати його. Щось на зразок цього:

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

Тобто, якщо припустити, що об’єкт, що має властивість, називається "theObject" :)


2

Ви також можете реалізувати веб-браузер, а потім завантажити html-файл, який містить javascript.

Потім перейдіть до document.InvokeScriptметоду в цьому браузері. Повернене значення функції eval можна вловити та перетворити на все, що вам потрібно.

Я зробив це в декількох Проектах, і це чудово працює.

Сподіваюся, це допоможе


0

Ви можете зробити це за допомогою функції прототипу:

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

і так далі...



0

Я написав пакет, 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 (), скомпільований на кожному, щоб дозволити створення дешевих копій, тобто використання виконуваного об’єкта, скомпільованого із сценарію або виразу, як шаблону для створення інших.

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


0

Я намагався отримати значення члена структури (класу) за його ім'ям. Структура не була динамічною. Усі відповіді не працювали, поки я нарешті не отримав:

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

Цей метод поверне значення члена за його іменем. Це працює за звичайною структурою (класом).


0

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


-1

На жаль, C # не має власних засобів для виконання саме того, про що ви просите.

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


Погляньте на Roslyn Scripting API
AlexMelw

-9

правильна відповідь - вам потрібно кешувати всі результати, щоб зберегти споживання пам'яті низьким.

приклад може виглядати так

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

збережіть ідентифікатор і використовуйте його в коді

сподіваюся, це допоможе


5
хтось плутав оцінку з пошуком. Якщо ви знаєте всі можливі програми (я думаю, що це принаймні NP-Hard) ... і у вас є супермашина для попередньої компіляції всіх можливих результатів ... і немає побічних ефектів / зовнішніх входів ... Так, ця ідея теоретично працює . Код, однак, є однією великою синтаксичною помилкою
sehe
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.