NashTech Blog

High-Performance Calculations in .NET Using Expression Trees and Caching

Table of Contents

In business applications where formulas are calculated frequently — such as in finance, insurance, or scientific domains — performance can suffer if the same formula is parsed and evaluated repeatedly.

.NET offers a powerful solution: Expression Trees combined with caching. This approach allows you to build and compile formulas once, and reuse them across millions of evaluations.


✅ Example: Solving the Quadratic Equation

Let’s use the classic quadratic formula as an example:

x = (-b ± √(b² - 4ac)) / (2a)

We’ll build this formula using expression trees, compile it to a fast delegate, and cache it for reuse.

🧠 Why Expression Trees?

  • They allow us to construct code dynamically at runtime.
  • Once compiled, the performance is similar to hand-written code.
  • We can cache the compiled result to avoid re-parsing logic every time.

🧪 Complete Working Code


using System;
using System.Collections.Generic;
using System.Linq.Expressions;

class Program
{
    // Cache to store compiled formulas
    static Dictionary<string, Delegate> _formulaCache = new();

    static void Main()
    {
        // Compile and cache the formula if not already cached
        var formula = GetQuadraticFormula();

        // Use the formula
        var roots = formula(1, -3, 2); // Solves x² - 3x + 2 = 0
        Console.WriteLine($"Root1: {roots.Item1}, Root2: {roots.Item2}");
    }

    static Func<double, double, double, (double, double)> GetQuadraticFormula()
    {
        if (_formulaCache.TryGetValue("quadratic", out var cached))
        {
            return (Func<double, double, double, (double, double)>)cached;
        }

        // Parameters: a, b, c
        var a = Expression.Parameter(typeof(double), "a");
        var b = Expression.Parameter(typeof(double), "b");
        var c = Expression.Parameter(typeof(double), "c");

        // Build the expression for (-b ± sqrt(b² - 4ac)) / (2a)

        var bSquared = Expression.Multiply(b, b);
        var fourAC = Expression.Multiply(Expression.Constant(4.0), Expression.Multiply(a, c));
        var discriminant = Expression.Subtract(bSquared, fourAC);
        var sqrtDiscriminant = Expression.Call(typeof(Math).GetMethod("Sqrt", new[] { typeof(double) })!, discriminant);

        var negB = Expression.Negate(b);
        var twoA = Expression.Multiply(Expression.Constant(2.0), a);

        var root1 = Expression.Divide(Expression.Add(negB, sqrtDiscriminant), twoA);
        var root2 = Expression.Divide(Expression.Subtract(negB, sqrtDiscriminant), twoA);

        // Return both roots as a tuple
        var tupleConstructor = typeof(ValueTuple<double, double>).GetConstructor(new[] { typeof(double), typeof(double) })!;
        var result = Expression.New(tupleConstructor, root1, root2);

        var lambda = Expression.Lambda<Func<double, double, double, (double, double)>>(
            result, a, b, c);

        var compiled = lambda.Compile();
        _formulaCache["quadratic"] = compiled;

        return compiled;
    }
}

🚀 Benefits

  • Performance: The formula is compiled once, then reused quickly with different inputs.
  • Flexibility: You can build many types of formulas dynamically and cache them efficiently.
  • Clean code: Business logic stays dynamic but still highly optimized.

📌 Final Thoughts

Expression trees are not just for LINQ providers — they are incredibly useful when building dynamic, reusable business rules or formulas. When combined with caching, they offer a clean and high-performance solution for calculation-heavy .NET applications.

Picture of Huỳnh Minh Tú

Huỳnh Minh Tú

Senior Software Engineer

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top