using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; namespace FormulaEnginePoC.CSharpProvider { public class ExpressionCompiler where TDelegate : Delegate { private readonly string _expressionCode; private readonly string _baseClass; private readonly List _assemblies = new List(); private readonly List _namespaces = new List(); private CodeCompiler _codeCompiler; private TDelegate _compiledDelegate; public CompilerError[] CompilerErrors => _codeCompiler.CompilerErrors.ToArray(); public TDelegate CompiledDelegate => _compiledDelegate ?? throw new InvalidOperationException("No compiled yet"); public ExpressionCompiler(string expressionCode, string baseClass = null) { _expressionCode = expressionCode; _baseClass = baseClass; } public void AddReferenceAssembly(params string[] assemblies) { _assemblies.AddRange(assemblies); } public void AddNamespace(params string[] namespaces) { _namespaces.AddRange(namespaces); } public void Compile() { _codeCompiler = new CodeCompiler(); var namespaceIncluding = string.Join(Environment.NewLine, _namespaces.Select(p => $"using {p};")); var typeName = $"ClassOnTheFly_{string.Join("", Guid.NewGuid().ToByteArray().Select(p => $"{p:X2}"))}"; var methodName = $"MethodOnTheFly_{string.Join("", Guid.NewGuid().ToByteArray().Select(p => $"{p:X2}"))}"; var delegateMethod = typeof(TDelegate).GetMethod("Invoke") ?? throw new MissingMethodException("Can not find method `Invoke' from Delegate"); var returnType = delegateMethod.ReturnType; var paramList = string.Join(",", delegateMethod.GetParameters().Select((p, i) => p.ParameterType.FullName + (i == 0 ? " p" : $" arg{i - 1}"))); var preCompileCode = $"{namespaceIncluding}" + $"public class {typeName} {(string.IsNullOrWhiteSpace(_baseClass) ? "" : $":{_baseClass}")}" + $"{{ public static {returnType.FullName} {methodName}({paramList})" + $" {{" + $" return {_expressionCode};" + $" }}" + $"}}"; _codeCompiler.Compile(typeName, methodName, preCompileCode, _assemblies.ToArray()); _compiledDelegate = _codeCompiler.CompiledDelegate; } } }