错误的if语句仍然执行

时间:2020-10-23 11:39:48

标签: c# .net-core

我有以下方法,该方法应在调用每个节点上的操作时遍历一棵树,并且每次其上移或下移层次结构时都要遍历一次。逻辑本身可以完美地工作,但是我意识到我的单元测试失败了,因为检查已经调用的元素的if语句无法完成逻辑上的意图。

public static void IterateWithHierarchyFeedback(TBase start,
    Func<TBase, IEnumerable<TBase>> childSelector,
    Action<TBase> invoker,
    Action stepUpInvocator,
    Action stepDownInvocator,
    bool invokeOnStart)
{
    Stack<DynamicIterator> iterationStack = new Stack<DynamicIterator>();
    iterationStack.Push(new DynamicIterator(start, childSelector(start).GetEnumerator()));

    while (iterationStack.Count > 0)
    {
        var current = iterationStack.Pop();
        // HERE it fails because current.Invoked = true but invoker() is still executed
        if (!current.Invoked && invokeOnStart || current.Current != start)
            invoker(current.Current);

        if (current.Enumerator == null || !current.Enumerator.MoveNext())
        {
            if (current.Current != start)
                stepUpInvocator();
            continue;
        }

        stepDownInvocator();

        current.Invoked = true;
        iterationStack.Push(current);
        iterationStack.Push(new DynamicIterator(current.Enumerator.Current,
            childSelector(current.Enumerator.Current)?.GetEnumerator()));
        continue;
    }
}

这是我的单元测试:

[测试] 公共异步任务TestIterateWithFeedback() { StringBuilder b =新的StringBuilder();

DynamicIterate<Tree>.Downwards.IterateWithHierarchyFeedback(_tree, t => t.Children,
    tree => b.Append(tree.ReturnValue.ToString()),
    () => b.Append('<'),
    () => b.Append('>'),
    true);

Assert.Warn(b.ToString());
const string expected = "1>2>3>4<>5<<>6<<>7>>";
Assert.AreEqual(expected, b.ToString());

}

在这里您看到输出不是应该的。这是因为invoker()是在已调用的元素上调用的,否则按顺序输出将是正确的:

  2)   Expected string length 20 but was 23. Strings differ at index 8.
  Expected: "1>2>3>4<>5<<>6<<>7>>"
  But was:  "1>2>3>4<3>5<3<2>6<2<>7<"

有人可以向我解释为什么会这样吗?我找到了this,但即使没有调试器,它也会发生。我还尝试将状态对象(DynamicIterator)从结构更改为类(因为我最初认为这可能是异步变化的问题。但这也没有改变。

1 个答案:

答案 0 :(得分:0)

我不确定问题出在哪里,但是迭代代码看起来比所需的更为复杂。我会提出这样的建议:

    public static IEnumerable<(T Node, int Level)> DepthFirstWithlevel<T>(
           T self, 
           Func<T, IEnumerable<T>> selector)
    {
        var stack = new Stack<(T Node, int Level)>();
        stack.Push((self, 0));
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;
            foreach (var child in selector(current.Node))
            {
                stack.Push((child, current.Level + 1));
            }
        }
    }

这将返回树中的每个节点以及该节点具有的树中的级别,其中0为根。如果您特别需要为每个级别更改调用方法,则可以使用单独的方法进行更改,如下所示:

    public static void IterateWithHierarchyFeedback<T>(
        T self, 
        Func<T, IEnumerable<T>> selector, 
        Action<T> invoker, 
        Action stepUp, 
        Action stepDown)
    {
        int currentLevel = 0;
        foreach (var (node, level) in DepthFirstWithLevel(self, selector))
        {
            while (currentLevel < level)
            {
                currentLevel++;
                stepDown();
            }

            while (currentLevel > level)
            {
                currentLevel--;
                stepUp();
            }

            invoker(node);
        }
    }

对于树

A - B - C
  |  ⌞ D
   ⌞E - F

它将打印A>E>F<B>DC,即它将首先遍历底部分支(在.Reverse()之后插入selector(current.Node)进行更改)。它将直接从F转到B,而无需重新访问A。