Search code examples
c#expressionlinq-expressionsvisitor-pattern

Stateful Expression visitors multiple run issue


Let's say I need to write an expression visitor, which also uses some injectable service, hence it has to have a public constructor, and cannot be simply wrapped in a static facade.

public class ProcessingVisitor : ExpressionVisitor {

  IProcessor _processor;
  public string Result { get; private set; }

  public ProcessingVisitor(IProcessor processor) {
    _processor = processor;
  }

  protected override Expression VisitBinary(BinaryExpression node)
  {
    // visit left and right
    // ... and do something with _processor
    Result += // ... append something to result
    return node;
  }
}

Now when I want to use this visitor, I would instantiate it and use it like this

var myExpression = ...;
var myVisitor = new ProcessingVisitor();
myVisitor.Visit(myExpression);
var result = myVisitor.Result;

Now imagine I - for example by accident - run Visit on another expression. Then Result will contain both results concatenated. How can I make such a visitor completely "fool proof"? Where could I reset the Result? I could override Visit, but I don't know inside if it's called first time, or it's called in the middle of the processing, so I can't reset it there.


Solution

  • Something like this might work (overriding Visit to track what your root node is):

    public class ProcessingVisitor : ExpressionVisitor
    {
    
        IProcessor _processor;
        private Expression _rootExpression = null;
        public string Result { get; private set; }
    
        public ProcessingVisitor(IProcessor processor)
        {
            _processor = processor;
        }
    
        protected override Expression VisitBinary(BinaryExpression node)
        {
            // visit left and right
            // ... and do something with _processor
            Result += "";// ... append something to result
            return node;
        }
        public override Expression Visit(Expression node)
        {
            if (_rootExpression == null)
            {
                _rootExpression = node;
                Result = null;
            }
    
            var toReturn = base.Visit(node);
    
            if (_rootExpression == node)
                _rootExpression = null;
            return toReturn;
        }
    }
    

    Alternatively, you can use an internal class to separate your initializing from your visiting:

    public class ProcessingVisitor : ExpressionVisitor
    {
    
        IProcessor _processor;
    
        #region Inner Class
        internal class _Implementation : ExpressionVisitor
        {
            IProcessor _processor;
            internal string Result { get; set; }
    
            internal _Implementation(IProcessor processor)
            {
                _processor = processor;
            }
    
            protected override Expression VisitBinary(BinaryExpression node)
            {
                // visit left and right
                // ... and do something with _processor
                Result += "";// ... append something to result
                return node;
            }
    
            internal Expression VisitFresh(Expression node)
            {
                Result = null;
                return base.Visit(node);
            }
        }
        #endregion
    
        public string Result { get; private set; }
        public ProcessingVisitor(IProcessor processor)
        {
            _processor = processor;
        }
    
        public override Expression Visit(Expression node)
        {
            var impl = new _Implementation(_processor);
            var toReturn = impl.VisitFresh(node);
            Result = impl.Result;
            return toReturn;
        }
    }