Best and shortest way to evaluate mathematical expressions

Viewed 19511
8 Answers

You can use Math-Expression-Evaluator library which implements Shunting Yard algorithm that I am author of. It supports simple expressions such as 2.5+5.9, 17.89-2.47+7.16, 5/2/2+1.5*3+4.58, expressions with parentheses (((9-6/2)*2-4)/2-6-1)/(2+24/(2+4)) and expressions with variables:

var a = 6;
var b = 4.32m;
var c = 24.15m;
var engine = new ExpressionEvaluator();
engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});

You can also pass parameters as named variables:

dynamic dynamicEngine = new ExpressionEvaluator();

var a = 6;
var b = 4.5m;
var c = 2.6m;

dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);

It supports .Net Standard 2.0 so can be used from .Net Core as well as .Net Full Framework projects and it doesn't have any external dependencies.

To dynamically compile code using the new Roslyn API's, and load the assembly in a .net core project;

    string finalSource = ...;
    IEnumerable<Assembly> references = ...;

    var compilation = CSharpCompilation.Create("Dynamic",
        new[] { 
            SyntaxFactory.ParseSyntaxTree(
                finalSource,
                CSharpParseOptions.Default
                    .WithLanguageVersion(LanguageVersion.Latest)
            ) },
        references.Select(a => MetadataReference.CreateFromFile(a.Location)),

        new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
            .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)
    );

    using var ms = new MemoryStream();
    var e = compilation.Emit(ms);
    if (!e.Success)
        throw new Exception("Compilation failed");
    ms.Seek(0, SeekOrigin.Begin);

    var context = new AssemblyLoadContext(null, true);
    var assembly = context.LoadFromStream(ms);

Note that along with any other types required by the source you are compiling. In order to load the compiled assembly within the same process, references will need to include;

    AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == "netstandard").Single(),
    typeof(object).Assembly
Related