Отменить/повторить шаблон памяти С#

Я пытаюсь реализовать функциональность Undo/Redo для узлов дерева в С# (компонент treeView). Я использовал шаблон сувенира, но у меня возникли проблемы с частью Redo. Я не вижу, где моя логика ошибочна. Вот несколько фрагментов кода

    private List<Memento> _mementoStateList= new List<Memento>();
    private List<Memento> _undoStateList= new List<Memento>();
    public Memento Memento { get{return null;}
        set{_mementoStateList.Add(value);} }

    public Memento Undo()
    {
        if (!_mementoStateList.Any()) return null;
        Memento m = _mementoStateList.Last();
        _undoStateList.Add(m);
        _mementoStateList.Remove(m);
        return m;
    }

    public Memento Redo()
    {
        if (!_undoStateList.Any()) return null;
        Memento m = _undoStateList.Last();
        _mementoStateList.Add(m);
        _undoStateList.Remove(m);
        return m;
    }

В моей форме перед удалением узла я вызываю метод SaveMemento(), который создает новый объект Memento, представляющий текущее состояние. Объект добавляется в _mementoStateList.

При отмене и повторении действия я вызываю описанные выше методы Undo() и Redo().

Я предполагаю, что не сохраняю состояния в нужный момент? Любой вклад высоко ценится!


person MonicaS    schedule 10.02.2017    source источник
comment
Когда вы устанавливаете новый сувенир, вы должны сбросить свой список состояний отмены... В противном случае этот список может получить очень странное содержимое в определенных случаях использования.   -  person JHBonarius    schedule 10.02.2017


Ответы (2)


Возможно, вам следует рассмотреть возможность реализации отмены/возврата с помощью шаблона Command, а с Memento, если вам нужно хранить много состояний (на самом деле вопрос сколько действий отмены вы будете поддерживать), тогда реализация может быть тяжелой.

person dstar55    schedule 11.02.2017

Когда вы создаете сувенир, вы должны сделать глубокое клонирование дерева объектов, иначе сувенир будет просто ссылкой на текущее состояние. В этом случае любое изменение состояния будет отражаться на всех сувенирах, разрушая любую попытку сохранить историю предыдущих (или потенциальных будущих) состояний.

person pid    schedule 10.02.2017
comment
Я действительно клонирую корневой элемент и сохраняю его как TreeNode. - person MonicaS; 10.02.2017
comment
Я думаю, что глубокое клонирование — это боль. Командная скороговорка — идеальный партнер памятного узора. - person jlvaquero; 10.02.2017
comment
Этого может быть недостаточно, потому что от корневого узла ссылки на более глубокие узлы по-прежнему указывают на текущее состояние. Если вы просто клонируете корень, это самое большее поверхностное клонирование. Для глубокого клонирования вы должны посетить дерево и клонировать все объекты (включая целые числа в коробках, если они у вас есть) в процессе. Единственным исключением являются строки, которые автоматически создаются при изменении. - person pid; 10.02.2017
comment
@jlvaquero Я согласен, что сохранение состояния также означает выделение большого количества памяти. В некоторых случаях это может быть неосуществимо, в то время как шаблон команды намного эффективнее, но требует более глубокого понимания того, что вы делаете. Недостатком является то, что для действий, которые не имеют и обратного действия, при ОТМЕНЕ вы должны повторить все предыдущие команды вплоть до полного сохранения состояния, чтобы восстановить правильное состояние. Таким образом, команды в памяти идеальны, если у каждого действия есть обратное действие, в противном случае это тоже усложняется. - person pid; 10.02.2017