ExpressionCompiler.cs 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. using System;
  2. using System.CodeDom.Compiler;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. namespace FormulaEnginePoC.CSharpProvider
  6. {
  7. public class ExpressionCompiler<TDelegate> where TDelegate : Delegate
  8. {
  9. private readonly string _expressionCode;
  10. private readonly string _baseClass;
  11. private readonly List<string> _assemblies = new List<string>();
  12. private readonly List<string> _namespaces = new List<string>();
  13. private CodeCompiler<TDelegate> _codeCompiler;
  14. private TDelegate _compiledDelegate;
  15. public CompilerError[] CompilerErrors => _codeCompiler.CompilerErrors.ToArray();
  16. public TDelegate CompiledDelegate => _compiledDelegate ?? throw new InvalidOperationException("No compiled yet");
  17. public ExpressionCompiler(string expressionCode, string baseClass = null)
  18. {
  19. _expressionCode = expressionCode;
  20. _baseClass = baseClass;
  21. }
  22. public void AddReferenceAssembly(params string[] assemblies)
  23. {
  24. _assemblies.AddRange(assemblies);
  25. }
  26. public void AddNamespace(params string[] namespaces)
  27. {
  28. _namespaces.AddRange(namespaces);
  29. }
  30. public void Compile()
  31. {
  32. _codeCompiler = new CodeCompiler<TDelegate>();
  33. var namespaceIncluding = string.Join(Environment.NewLine, _namespaces.Select(p => $"using {p};"));
  34. var typeName = $"ClassOnTheFly_{string.Join("", Guid.NewGuid().ToByteArray().Select(p => $"{p:X2}"))}";
  35. var methodName = $"MethodOnTheFly_{string.Join("", Guid.NewGuid().ToByteArray().Select(p => $"{p:X2}"))}";
  36. var delegateMethod = typeof(TDelegate).GetMethod("Invoke") ?? throw new MissingMethodException("Can not find method `Invoke' from Delegate");
  37. var returnType = delegateMethod.ReturnType;
  38. var paramList = string.Join(",", delegateMethod.GetParameters().Select((p, i) => p.ParameterType.FullName + (i == 0 ? " p" : $" arg{i - 1}")));
  39. var preCompileCode =
  40. $"{namespaceIncluding}" +
  41. $"public class {typeName} {(string.IsNullOrWhiteSpace(_baseClass) ? "" : $":{_baseClass}")}" +
  42. $"{{ public static {returnType.FullName} {methodName}({paramList})" +
  43. $" {{" +
  44. $" return {_expressionCode};" +
  45. $" }}" +
  46. $"}}";
  47. _codeCompiler.Compile(typeName, methodName, preCompileCode, _assemblies.ToArray());
  48. _compiledDelegate = _codeCompiler.CompiledDelegate;
  49. }
  50. }
  51. }