Last active
November 30, 2019 16:17
-
-
Save bradjolicoeur/8627548 to your computer and use it in GitHub Desktop.
Csharp Dynamic Script Engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.CodeDom.Compiler; | |
using System.Reflection; | |
using Microsoft.CSharp; | |
public class ScriptEngine | |
{ | |
private AppDomain domain = null; | |
private dynamic evaluator = null; | |
private Type evaluatorType = null; | |
private string source { get; set; } | |
public string Source { get { return source; } } | |
private StringBuilder variables { get; set; } | |
public string Code {get;set;} | |
public string[] Messages = null; | |
public ScriptEngine() | |
{ | |
domain = AppDomain.CreateDomain("ScriptEngine"); | |
variables = new StringBuilder(); | |
} | |
public void Unload() | |
{ | |
if (domain != null) | |
{ | |
AppDomain.Unload(domain); | |
domain = null; | |
} | |
} | |
public void AddProperty(string PropertyName, string PropertyType) | |
{ | |
//refactor to be true property | |
const string propertyFormat = "public {0} {1} {{get; set;}}"; | |
variables.AppendLine(string.Format(propertyFormat, PropertyType, PropertyName)); | |
} | |
public void SetProperty(string PropertyName, object Value) | |
{ | |
object o = evaluatorType.InvokeMember( | |
PropertyName, | |
BindingFlags.SetProperty, | |
null, | |
evaluator, | |
new object[] { Value } | |
); | |
} | |
public object GetProperty(string PropertyName) | |
{ | |
object o = evaluatorType.InvokeMember( | |
PropertyName, | |
BindingFlags.GetProperty, | |
null, | |
evaluator, | |
new object[] { } | |
); | |
return o; | |
} | |
public bool Compile() | |
{ | |
Messages = null; | |
var compiler = new CSharpCodeProvider(); | |
//Concat source code | |
source = GenSource(); | |
CompilerParameters parameters = new CompilerParameters(); | |
parameters.GenerateInMemory = true; | |
CompilerResults results = compiler.CompileAssemblyFromSource(parameters, source); | |
// Check for compile errors / warnings | |
if (results.Errors.HasErrors || results.Errors.HasWarnings) | |
{ | |
Messages = new string[results.Errors.Count]; | |
for (int i = 0; i < results.Errors.Count; i++) | |
Messages[i] = results.Errors[i].ToString(); | |
return false; | |
} | |
else | |
{ | |
Assembly assembly = results.CompiledAssembly; | |
Type[] tt = assembly.GetTypes(); | |
evaluatorType = assembly.GetType("UserScript.RunScript"); | |
evaluator = Activator.CreateInstance(evaluatorType); | |
return true; | |
} | |
} | |
private string GenSource() | |
{ | |
const string sourceFormat = @"namespace UserScript | |
{{ | |
using System; | |
using System.Text; | |
public class RunScript | |
{{ | |
//variables | |
{0} | |
public object Eval() | |
{{ | |
object Result = null; | |
//Code | |
{1} | |
return Result; | |
}} | |
}} | |
}}"; | |
return string.Format(sourceFormat, variables.ToString(), Code); | |
} | |
/// <summary> | |
/// Execute the 'Eval' method and return results | |
/// </summary> | |
/// <returns></returns> | |
public object Evaluate() | |
{ | |
return (evaluator != null) ? evaluator.Eval() : null; //dynamic object invoke | |
} | |
public T Evaluate<T>() | |
{ | |
var action = (Func<T>)Delegate.CreateDelegate(typeof(Func<T>), null, "Eval"); | |
return action(); // delegate | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment