2016-01-14 2 views
3

Предположим, мне нужно написать выражение посетителя, которое также использует некоторую инъекционную услугу, следовательно, он должен иметь открытый конструктор и не может быть просто завернут в статический фасад.Stateful Expression посетителей многократная проблема запуска

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; 
    } 
} 

Теперь, когда я хочу использовать этот посетитель, я бы его экземпляр и использовать его как этот

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

Теперь представьте себе, я - например, случайно - запустить Visit на другое выражение. Тогда Result будет содержать оба конкатенированных результата. Как я могу сделать такого посетителя полностью «безумным доказательством»? Где я могу сбросить Result? Я мог бы переопределить Visit, но я не знаю внутри, если он вызван в первый раз, или он вызван в середине обработки, поэтому я не могу сбросить его там.

+0

Не могли бы вы разместить код для 'Visit' - может быть, он находится в' ExpressionVisitor'? Кроме того, правильный конструктор 'ProcessingVisitor' корректен? – jnovo

+0

Да, это потомок потомков Expression. Конструктор без параметров - ошибка, я исправлю его, спасибо. –

+0

С точки зрения чистой архитектуры ваша проблема заключается в том, что вы добавили состояние в свой класс, но вы обнаружите, что состояние может привести к недействительному состоянию. Решение прост. Удалите состояние. Сделать вызов метода для синтаксического анализа дерева выражений, возвращающего строку, или путаницы с посещенным выражением и строкой – Aron

ответ

1

Что-то подобное может работать (переопределение Visit, чтобы отслеживать, что ваш корневой узел):

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; 
    } 
} 

В качестве альтернативы, вы можете использовать внутренний класс, чтобы отделить вашу инициализацию от вашего посещения:

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; 
    } 
} 
+0

Благодарим вас за ответ, да, это своего рода решение, но все же не доказательство дурака, так как наивно можно использовать метод Visit. И C# не предлагает способ скрыть публично унаследованный элемент. –

+0

@ ZoltánTamási См. Редактирование. – Shlomo

+0

Мне нравится ваша первая идея отслеживать состояние посетителя. Однако я бы немного его улучшил. Вместо отслеживания корневого выражения мы могли бы ввести простое целочисленное поле, которое увеличивалось бы до «base.Visit» и уменьшалось впоследствии. Мы могли бы проверить, было ли это исходное состояние, сравнивая это поле с 0. Я думаю, что это было бы более простым и чистым. Как вы думаете? –

Смежные вопросы