Table of Contents

Overriding Evaluation Behavior

NCalc provides a flexible way to evaluate expressions, and in some cases, you may need to customize its evaluation behavior. This can be done by creating a custom Expression and overriding CreateEvaluationVisitor or CreateAsyncEvaluationVisitor.

Custom Evaluation Visitor

By creating a custom evaluation visitor, you can modify how specific expressions are interpreted. The following example overrides the behavior of value expressions, returning a special string when encountering the number 42:

private class CustomVisitor(ExpressionContext context, CancellationToken cancellationToken) : EvaluationVisitor(context, cancellationToken)
{
    public override object? Visit(ValueExpression expression)
    {
        // My custom behavior.
        if(expression.Value is 42)
            return "The answer";

        // Use the normal behavior.
        return base.Visit(expression);
    }
}

private class CustomAsyncVisitor(ExpressionContext context, CancellationToken cancellationToken) : AsyncEvaluationVisitor(context, cancellationToken)
{
    public override Task<object?> Visit(ValueExpression expression)
    {
        // My custom behavior.
        if(expression.Value is 42)
            return Task.FromResult<object?>("The answer async");

        // Use the normal behavior.
        return base.Visit(expression, cancellationToken);
    }
}

Custom Expression

To integrate the custom visitor with NCalc, inherit from Expression and override the visitor creation method:

internal class CustomExpression(
    string expression,
    ExpressionContext context,
    ILogicalExpressionFactory logicalExpressionFactory,
    ILogicalExpressionCache cache) : Expression(expression, context, logicalExpressionFactory, cache)
{
    public CustomExpression(
        LogicalExpression logicalExpression,
        ExpressionContext context,
        ILogicalExpressionFactory logicalExpressionFactory,
        ILogicalExpressionCache cache)
        : this(string.Empty, context, logicalExpressionFactory, cache)
    {
        LogicalExpression = logicalExpression;
    }

    protected override EvaluationVisitor CreateEvaluationVisitor()
    {
        return new CustomVisitor(Context);
    }

    protected override AsyncEvaluationVisitor CreateAsyncEvaluationVisitor()
    {
        return new CustomAsyncVisitor(Context);
    }
}

Using IExpressionFactory

Use a custom implementation of IExpressionFactory to return your custom expression type:

internal class CustomExpressionFactory(
    ILogicalExpressionFactory logicalExpressionFactory,
    ILogicalExpressionCache cache) : IExpressionFactory
{
    public Expression Create(string expression, ExpressionContext? expressionContext = null)
    {
        return new CustomExpression(expression, expressionContext ?? new(), logicalExpressionFactory, cache);
    }

    public Expression Create(LogicalExpression logicalExpression, ExpressionContext? expressionContext = null)
    {
        return new CustomExpression(logicalExpression, expressionContext ?? new(), logicalExpressionFactory, cache);
    }
}

Register the factory with dependency injection:

services.AddNCalc()
        .WithExpressionFactory<CustomExpressionFactory>();