c#在推送之前检查堆栈中的最后一项是否等于X?

c# Check if Last item in stack equals X before pushing?

所以我希望我的问题是有道理的,但我正在 Unity 中开发一个基本的 undo/redo 系统,我试图防止添加“空”迭代(基本上如果一个对象没有t 改变位置)。

我正在为 Undo/Redo 系统使用典型的堆栈工作流,这是我将方法添加到撤消堆栈的方法:

public static void AddAction(UndoableChange action)
{
    redoStack.Clear();
    undoStack.Push(action);
}

我正在添加可撤消的更改,当一个对象在框架上被“选中”时,鼠标处于按下状态,然后在鼠标处于上升状态时再次被“选中”。但在我这样做之前,我想确保它不会记录我只是随机点击屏幕(这导致我不得不在实际撤消被调用之前多次撤消)。
如果堆栈中的 last/most 最近项不等于我尝试推送的 UndoableChange,语法是什么?

我试过了,但它仍然记录“空”点击:

public static void AddAction(UndoableChange action)
{
    if (undoStack.Count >0 && undoStack.Peek().Equals(action))
        return;

    redoStack.Clear();
    undoStack.Push(action);
}

这个好像应该比较简单吧。任何帮助将不胜感激!


完整脚本如下

    public struct ObjectState
{
    // The transform this data belongs to
    private Transform transform;

    private Vector3 localPosition;
    private Quaternion localRotation;
    private Vector3 localScale;

    private bool active;

    public ObjectState(GameObject obj)
    {
        transform = obj.transform;
        localPosition = transform.localPosition;
        localRotation = transform.localRotation;
        localScale = transform.localScale;

        active = obj.activeSelf;
    }

    public void Apply()
    {
        transform.localPosition = localPosition;
        transform.localRotation = localRotation;
        transform.localScale = localScale;

        transform.gameObject.SetActive(active);
    }
}
public struct UndoableChange
{
    private ObjectState _before;
    private ObjectState _after;

    public UndoableChange(ObjectState before, ObjectState after)
    {
        _before = before;
        _after = after;
    }

    public void Undo()
    {
        _before.Apply();
    }

    public void Redo()
    {
        _after.Apply();
    }
}

public static class UndoRedoControls
{
    private static Stack<UndoableChange> undoStack = new Stack<UndoableChange>();
    private static Stack<UndoableChange> redoStack = new Stack<UndoableChange>();

    public static void Undo()
    {
        if (undoStack.Count == 0) return;

        var lastAction = undoStack.Pop();

        lastAction.Undo();

        redoStack.Push(lastAction);
        Debug.Log("UNDOING");
    }

    public static void Redo()
    {
        if (redoStack.Count == 0) return;

        var lastAction = redoStack.Pop();

        lastAction.Redo();

        undoStack.Push(lastAction);
        Debug.Log("REDOING");
    }

    public static void AddAction(UndoableChange action)
    {
        if (undoStack.Count >0 && undoStack.Peek().Equals(action))
            return;

        redoStack.Clear();
        undoStack.Push(action);
    }
}

默认情况下,Equals 检查 引用相等性 类,这不是你想做的,也不是你想做的 struct 默认失败,因为它们是值类型,因此永远不会是同一个实例。

您想查看 beforeafter 值是否不同。

您当然可以实施任何检查相等性的方法,但最佳做法是实施 IEquatable<T>

因此您可以使用诸如

之类的东西
public struct ObjectState : IEquatable<ObjectState>
{
    // The transform this data belongs to
    public readonly Transform transform;

    private readonly Vector3 localPosition;
    private readonly Quaternion localRotation;
    private readonly Vector3 localScale;

    private readonly bool active;

    public ObjectState(GameObject obj)
    {
        transform = obj.transform;
        localPosition = transform.localPosition;
        localRotation = transform.localRotation;
        localScale = transform.localScale;

        active = obj.activeSelf;
    }

    public void Apply()
    {
        transform.localPosition = localPosition;
        transform.localRotation = localRotation;
        transform.localScale = localScale;

        transform.gameObject.SetActive(active);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is ObjectState objState)) return false;
        return Equals(objState);
    }

    public bool Equals(ObjectState other)
    { 
        if(other.transform != transform) return false;

        // For now using the approximations Unity uses by default
        // with precision of 0.00001
        return other.localPosition == localPosition && other.localRotation == localRotation && other.localScale == localScale;
    }

    public static bool operator ==(ObjectState a, ObjectState b)
    {  
        return a.Equals(b);
    }

    public static bool operator !=(ObjectState a, ObjectState b)
    {
        return !a.Equals(b); 
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = transform.GetHashCode();
            hash = 31 * hash + localPosition.GetHashCode();
            hash = 31 * hash + localRotation.GetHashCode();
            return 31 * hash + localScale.GetHashCode(); 
        }
    }
}

现在您可以实际检查两个 ObjectState 实例是否相等。

现在你可以使用这个了

public struct UndoableChange : IEquatable<UndoableChange>
{
    private ObjectState _before;
    private ObjectState _after;

    // Check if this is a valid action
    // It refers to the same object
    // and actually something was changed
    public bool IsValid()
    {
        return _before.transform == _after.transform && _before != _after;
    }

    public UndoableChange(ObjectState before, ObjectState after)
    {
        _before = before;
        _after = after;
    }

    public void Undo()
    {
        _before.Apply();
    }

    public void Redo()
    {
        _after.Apply();
    }

    public override bool Equals (object obj)
    {
        if(!(obj is UndoableChange other)) return false;
        return Equals (other);
    }

    public bool Equals (UndoableChange other)
    {
        return other._before == _before && other._after == _after;
    }

    public static bool operator ==(UndoableChange a, UndoableChange b)
    {
        return a.Equals(b);
    }

    public static bool operator !=(UndoableChange a, UndoableChange b)
    {
        return !a.Equals(b);
    }

    public override int GetHashCode ()
    {
        unchecked
        {
            return _before.GetHashCode() * 31 + _after.GetHashCode();
        }
    }
}

现在你可以检查两件事

if(action.IsValid())

if(undoStack.Peek() != action)