mercredi 16 novembre 2016

Clean way to visit nodes without casting

I've got two opposite things to do. Let's consider such implementation. I build DepthFirstEnumerator to iterate over tree. For now what I want to achieve is to use this iterator with visitors which will be implemented in classes that inherits from Node class. Here is how i implement it:

public abstract class Node : IEnumerable
{
    private Node parent;
    private List<Node> descendants;

    public Node(Node parent)
    {
        this.parent = parent;
        this.descendants = new List<Node>();
    }

    public IReadOnlyList<Node> Descendants => descendants;
    public bool IsRoot => parent == null;
    public bool IsLeaf => descendants.Count == 0;
    public Node Parent => parent;
    public int Count => descendants.Count;

    public Node this[int index]
    {
        get
        {
            return descendants[index];
        }
    }

    public Node Root
    {
        get
        {
            var p = this;
            while (p.parent != null)
                p = p.parent;
            return p;
        }
    }

    public void AddChild(Node child)
    {
        child.SetParent(this);
        descendants.Add(child);
    }

    public void RemoveChild(Node child)
    {
        var node = descendants.FirstOrDefault(e => e.Equals(child));
        if (node != null)
            descendants.Remove(node);
    }

    protected void SetParent(Node parent)
    {
        this.parent = parent;
    }

    public virtual IEnumerator GetEnumerator() => new DepthFirstEnumerator(this);
}

this is my enumerator:

public class DepthFirstEnumerator : IEnumerator<Node>
{
    private Node node;
    private Node current;

    private Stack<Node> stack;

    public DepthFirstEnumerator(Node node)
    {
        this.node = node;
        Reset();
    }

    public Node Current => current;

    object IEnumerator.Current => current;

    public void Dispose()
    { }

    public bool MoveNext()
    {
        stack.Push(current);
        while(stack.Count > 0)
        {
            var node = stack.Pop();
            foreach (var i in node.Descendants.Reverse())
                stack.Push(i);
            current = node;
            return true;
        }
        return false;
    }

    public void Reset()
    {
        current = node;
    }
}

what I need now is to make visitors and visit all nodes in enumerator order.

public class ExpressionVisitor
{
    public void Accept(TestExpressionNode node)
    { }
}

public class TestExpressionNode : ExpressionNode<ExpressionVisitor>
{
    public override void Accept(ExpressionVisitor visitor)
    {
        visitor.Accept(this);
    }

    public override IEnumerator GetEnumerator() => new DepthFirstEnumerator(this);
}

but I can't call Accept(...) becouse my type is Node. I'm looking for clean way to visit nodes becouse right now, it force me to cast Node to TestExpressionNode.

Im wondering if it's possible

Aucun commentaire:

Enregistrer un commentaire