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.