Last active
April 3, 2024 14:11
-
-
Save RickStrahl/f65727881668488b0a562df4c21ab560 to your computer and use it in GitHub Desktop.
A few different approaches to dynamically execute C# code dynamically at runtime from a string of code.
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 Microsoft.CodeAnalysis.CSharp; | |
using Microsoft.CodeAnalysis.CSharp.Scripting; | |
using Microsoft.CodeAnalysis.Scripting; | |
using Mono.CSharp; | |
using System; | |
using System.CodeDom.Compiler; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Dynamic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace ConsoleApp2 | |
{ | |
public class Program | |
{ | |
public enum CompilerModes | |
{ | |
ClassicCodeProvider, | |
RoslynCodeProvider, | |
RoslynScripting, | |
MonoEvaluator | |
} | |
static void Main(string[] args) | |
{ | |
var mode = CompilerModes.ClassicCodeProvider; | |
Console.WriteLine("Starting..."); | |
var swatch = new Stopwatch(); | |
swatch.Start(); | |
RunScript(mode); | |
swatch.Stop(); | |
Console.WriteLine("First Elapsed ms: " + swatch.ElapsedMilliseconds); | |
for (int x = 0; x < 2; x++) | |
{ | |
swatch.Reset(); | |
swatch.Start(); | |
RunScript(mode); | |
swatch.Stop(); | |
Console.WriteLine("Elapsed ms: " + swatch.ElapsedMilliseconds); | |
} | |
swatch.Stop(); | |
Console.WriteLine("Elapsed ms: " + swatch.ElapsedMilliseconds); | |
Console.ReadKey(); | |
} | |
public static void RunScript(CompilerModes mode) | |
{ | |
switch (mode) | |
{ | |
case CompilerModes.ClassicCodeProvider: | |
ClassicSharpCodeProvider(); | |
break; | |
case CompilerModes.RoslynCodeProvider: | |
RoslynCodeDomProvider(); | |
break; | |
case CompilerModes.RoslynScripting: | |
RoslynScripting(); | |
break; | |
case CompilerModes.MonoEvaluator: | |
MonoCompiler(); | |
break; | |
} | |
} | |
/// <summary> | |
/// Using the class CSharpCode Provider | |
/// </summary> | |
public static void ClassicSharpCodeProvider() | |
{ | |
var Compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler(); | |
var parms = new CompilerParameters(); | |
parms.ReferencedAssemblies.Add("System.dll"); | |
parms.ReferencedAssemblies.Add("System.Core.dll"); | |
parms.GenerateInMemory = true; | |
var classCode = @" | |
using System; | |
namespace MyApp | |
{ | |
public class Test1 | |
{ | |
public string HelloWorld(string name) | |
{ | |
//return $@""Hello {name}""; // This doesn't work in in classic - C# 6+ syntax | |
return ""Hello "" + name + "" from the classic compiler.""; | |
} | |
} | |
} | |
"; | |
CompilerResults result = Compiler.CompileAssemblyFromSource(parms, classCode); | |
if (result.Errors.Count > 0) | |
{ | |
Console.WriteLine("*** Compilation Errors"); | |
foreach (var error in result.Errors) | |
{ | |
Console.WriteLine("- " + error); | |
return; | |
} | |
} | |
var ass = result.CompiledAssembly; | |
dynamic inst = ass.CreateInstance("MyApp.Test1"); | |
string methResult = inst.HelloWorld("Rick") as string; | |
Console.WriteLine(methResult); | |
} | |
/// <summary> | |
/// This uses a CodeDomProvider which has pretty much the same syntax as the | |
/// classic code but uses a CodeDomProvider, rather then a CodeDomCompiler. | |
/// </summary> | |
public static void RoslynCodeDomProvider() | |
{ | |
var provider = CodeDomProvider.CreateProvider("CSharp"); | |
var parms = new CompilerParameters(); | |
parms.ReferencedAssemblies.Add("System.dll"); | |
parms.ReferencedAssemblies.Add("System.Core.dll"); | |
parms.ReferencedAssemblies.Add("Microsoft.CSharp.dll"); | |
parms.GenerateInMemory = true; | |
var classCode = @" | |
using System; | |
namespace MyApp | |
{ | |
public class Test1 | |
{ | |
public string HelloWorld(string name) | |
{ | |
return $@""Hello {name} from Roslyn with CodeDomProvider.""; | |
} | |
} | |
} | |
"; | |
CompilerResults result = provider.CompileAssemblyFromSource(parms, classCode); | |
if (result.Errors.Count > 0) | |
{ | |
Console.WriteLine("*** Compilation Errors"); | |
foreach (var error in result.Errors) | |
{ | |
Console.WriteLine("- " + error); | |
return; | |
} | |
} | |
var ass = result.CompiledAssembly; | |
dynamic inst = ass.CreateInstance("MyApp.Test1"); | |
string methResult = inst.HelloWorld("Rick") as string; | |
Console.WriteLine(methResult); | |
} | |
/// <summary> | |
/// Model parameter to pass to the Mono Code as a static | |
/// ConsoleApp2.Program.Model that's accessible in the script | |
/// </summary> | |
[ThreadStatic] | |
public static dynamic Model = new ExpandoObject(); | |
/// <summary> | |
/// Using the Mono Compiler Server (MCS) to evaluate script | |
/// | |
/// Code is evaluated on the fly (no assemblies generated) | |
/// </summary> | |
public static void MonoCompiler() | |
{ | |
var settings = new CompilerSettings | |
{ | |
StdLib = true, | |
}; | |
settings.AssemblyReferences.Add("System.dll"); | |
var reportPrinter = new ConsoleReportPrinter(); | |
var ctxt = new CompilerContext(settings, reportPrinter); | |
var eval = new Evaluator(ctxt); | |
eval.ReferenceAssembly(Assembly.GetExecutingAssembly()); | |
// Add Namespaces | |
eval.Run("using System;"); | |
// Pass a parameter via a known Model type | |
Model.Name = "Rick Static"; | |
// Handle picking up model parameter and pick out name | |
eval.Run(@"dynamic model = ConsoleApp2.Program.Model; | |
string name = model.Name"); | |
// Main body of the code to execute | |
var code = @" | |
string helloText = @""Hello "" + name + "" from Mono Evaluator."""; | |
// execute the method body code | |
eval.Run(code); | |
// pick up result value | |
eval.Evaluate("helloText", out object result, out bool resultSet); | |
string resultString = result as string; | |
Console.WriteLine(resultString); | |
} | |
public class ParmModel | |
{ | |
public string Name { get; set; } | |
} | |
public static void RoslynScripting() | |
{ | |
string methodCode = @" | |
string helloString = $@""Hello {Name}, from Roslyn with CSharpScripting.""; | |
"; | |
var model = new ParmModel { Name = "Rick" }; | |
var opt = ScriptOptions.Default; | |
opt.AddReferences(typeof(string).Assembly, typeof(ParmModel).Assembly); | |
opt.AddImports("System"); | |
var state = CSharpScript.RunAsync(methodCode, opt, model, model.GetType()).Result; | |
string result = state.Variables.FirstOrDefault(v => v.Name == "helloString")?.Value as string; | |
Console.WriteLine(result); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, what version of .net is this code for, because I am having trouble running it.