Skip to content

Instantly share code, notes, and snippets.

@ahmad-moussawi
Forked from RickStrahl/CompilingCSharpCode.cs
Created January 29, 2020 08:40
Show Gist options
  • Save ahmad-moussawi/32331b9643ecc310447c01d7623d553f to your computer and use it in GitHub Desktop.
Save ahmad-moussawi/32331b9643ecc310447c01d7623d553f 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.
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