Note: Mathematical expression evaluation is not the focus of this question. I want to compile and execute new code at runtime in .NET. That being said...
I would like to allow the user to enter any equation, like the following, into a text box:
x = x / 2 * 0.07914
x = x^2 / 5
And have that equation applied to incoming data points. The incoming data points are represented by x and each data point is processed by the user-specified equation. I did this years ago, but I didn't like the solution because it required parsing the text of the equation for every calculation:
float ApplyEquation (string equation, float dataPoint)
{
// parse the equation string and figure out how to do the math
// lots of messy code here...
}
When you're processing boatloads of data points, this introduces quite a bit of overhead. I would like to be able to translate the equation into a function, on the fly, so that it only has to be parsed once. It would look something like this:
FunctionPointer foo = ConvertEquationToCode(equation);
....
x = foo(x); // I could then apply the equation to my incoming data like this
Function ConvertEquationToCode would parse the equation and return a pointer to a function that applies the appropriate math.
The app would basically be writing new code at run time. Is this possible with .NET?
Yes! Using methods found in the Microsoft.CSharp, System.CodeDom.Compiler, and System.Reflection name spaces. Here is a simple console app that compiles a class ("SomeClass") with one method ("Add42") and then allows you to invoke that method. This is a bare-bones example that I formatted down to prevent scroll bars from appearing in the code display. It is just to demonstrate compiling and using new code at run time.
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Reflection;
namespace RuntimeCompilationTest {
class Program
{
static void Main(string[] args) {
string sourceCode = @"
public class SomeClass {
public int Add42 (int parameter) {
return parameter += 42;
}
}";
var compParms = new CompilerParameters{
GenerateExecutable = false,
GenerateInMemory = true
};
var csProvider = new CSharpCodeProvider();
CompilerResults compilerResults =
csProvider.CompileAssemblyFromSource(compParms, sourceCode);
object typeInstance =
compilerResults.CompiledAssembly.CreateInstance("SomeClass");
MethodInfo mi = typeInstance.GetType().GetMethod("Add42");
int methodOutput =
(int)mi.Invoke(typeInstance, new object[] { 1 });
Console.WriteLine(methodOutput);
Console.ReadLine();
}
}
}