How can I evaluate C# code dynamically?

2018-12-31 06:51发布

I can do an eval("something()"); to execute the code dynamically in JavaScript. Is there a way for me to do the same thing in C#?

An example of what I am trying to do is: I have an integer variable (say i) and I have multiple properties by the names: "Property1", "Property2", "Property3", etc. Now, I want to perform some operations on the " Propertyi " property depending on the value of i.

This is really simple with Javascript. Is there any way to do this with C#?

16条回答
荒废的爱情
2楼-- · 2018-12-31 07:29

This is an eval function under c#. I used it to convert anonymous functions (Lambda Expressions) from a string. Source: 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;

}
查看更多
栀子花@的思念
3楼-- · 2018-12-31 07:32

I have written a package, SharpByte.Dynamic, to simplify the task of compiling and executing code dynamically. The code can be invoked on any context object using extension methods as detailed further here.

For example,

someObject.Evaluate<int>("6 / {{{0}}}", 3))

returns 3;

someObject.Evaluate("this.ToString()"))

returns the context object's string representation;

someObject.Execute(@
"Console.WriteLine(""Hello, world!"");
Console.WriteLine(""This demonstrates running a simple script"");
");

runs those statements as a script, etc.

Executables can be gotten easily using a factory method, as seen in the example here--all you need is the source code and list of any expected named parameters (tokens are embedded using triple-bracket notation, such as {{{0}}}, to avoid collisions with string.Format() as well as Handlebars-like syntaxes):

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

Each executable object (script or expression) is thread-safe, can be stored and reused, supports logging from within a script, stores timing information and last exception if encountered, etc. There is also a Copy() method compiled on each to allow creating cheap copies, i.e. using an executable object compiled from a script or expression as a template for creating others.

Overhead of executing an already-compiled script or statement is relatively low, at well under a microsecond on modest hardware, and already-compiled scripts and expressions are cached for reuse.

查看更多
千与千寻千般痛.
4楼-- · 2018-12-31 07:32

You might check the Heleonix.Reflection library. It provides methods to get/set/invoke members dynamically, including nested members, or if a member is clearly defined, you can create a getter/setter (lambda compiled into a delegate) which is faster than reflection:

var success = Reflector.Set(instance, null, $"Property{i}", value);

Or if number of properties is not endless, you can generate setters and chache them (setters are faster since they are compiled delegates):

var setter = Reflector.CreateSetter<object, object>($"Property{i}", typeof(type which contains "Property"+i));
setter(instance, value);

Setters can be of type Action<object, object> but instances can be different at runtime, so you can create lists of setters.

查看更多
浅入江南
5楼-- · 2018-12-31 07:33

the correct answer is you need to cache all the result to keep the mem0ry usage low.

an example would look like this

TypeOf(Evaluate)
{
"1+1":2;
"1+2":3;
"1+3":5;
....
"2-5":-3;
"0+0":1
} 

and add it to a List

List<string> results = new List<string>();
for() results.Add(result);

save the id and use it in the code

hope this helps

查看更多
登录 后发表回答