StrongBox 的具体用法

问题描述 投票:0回答:3

我正在使用 Reflector 查看 Roslyn 2012 年 9 月 CTP,我注意到语法树的以下深度优先遍历:

private IEnumerable<CommonSyntaxNode> DescendantNodesOnly(TextSpan span,
    Func<CommonSyntaxNode, bool> descendIntoChildren, bool includeSelf)
{
    if (includeSelf && IsInSpan(span, FullSpan))
    {
        yield return this;
    }

    if ((descendIntoChildren != null) && !descendIntoChildren(this))
    {
        yield break;
    }

    var queue = new Queue<StrongBox<IEnumerator<CommonSyntaxNode>>>();
    var stack = new Stack<StrongBox<IEnumerator<CommonSyntaxNode>>>();
    stack.Push(new StrongBox<IEnumerator<CommonSyntaxNode>>(ChildNodes().GetEnumerator()));
    while (stack.Count > 0)
    {
        var enumerator = stack.Peek();
        StrongBox<IEnumerator<CommonSyntaxNode>> childEnumerator;
        if (enumerator.Value.MoveNext())
        {
            var current = enumerator.Value.Current;
            if (IsInSpan(span, current.FullSpan))
            {
                yield return current;

                if ((descendIntoChildren == null) || descendIntoChildren(current))
                {
                    childEnumerator = queue.Count == 0
                        ? new StrongBox<IEnumerator<CommonSyntaxNode>>()
                        : queue.Dequeue();
                    childEnumerator.Value = current.ChildNodes().GetEnumerator();
                    stack.Push(childEnumerator);
                }
            }
        }
        else
        {
            childEnumerator = stack.Pop();
            childEnumerator.Value = null;
            queue.Enqueue(childEnumerator);
        }
    }
}

我猜测队列是为了缓解运行时分配和取消分配这么多

IEnumerator<CommonSyntaxNode>
实例的情况。

但是,我不确定为什么

IEnumerator<CommonSyntaxNode>
被包裹在
StrongBox<>
中。将通常是值类型的
IEnumerator<CommonSyntaxNode>
包装在引用类型
StrongBox<>
中涉及到什么样的性能和安全权衡?

roslyn
3个回答
3
投票

CommonSyntaxNode
是一个抽象类,包含很多值类型,可以继承到一个大对象中。

IEnumerator<CommonSyntaxNode>
仅包含对
CommonSyntaxNode
的引用,因此看起来
CommonSyntaxNode
的大小不会影响
Enumerator
的大小,因为它只是一个引用,但是

由于

IEnumerator<T>
的方法
MoveNext()
使用
yield return;
Enumerator
中的每次迭代都会导致该方法保存其状态直到下一次迭代。

由于整个方法状态足够重,并且它可能包含

CommonSyntaxNode
的属性以便执行
MoveNext()
逻辑,因此整个
IEnumerator<CommonSyntaxNode>
可能会占用大量内存。

使用

StrongBox<>
会导致
Queue
Stack
只保留一个小的引用对象(
StrongBox<>
而不是潜在的占用大量内存的
IEnumerator<CommonSyntaxNode>
- 因此 - GC 正在清理
Queue
Stack
包含内存中的
IEnumerator<CommonSyntaxNode>
,速度可能更快 - 减少应用程序总内存消耗。

请注意,

CommonSyntaxNode
的枚举器是一个结构体,直接使用它意味着深度复制整个结构体,它是一个小结构体,所以它不是很重,但仍然......


3
投票

StrongBox<T>
的优点是一旦一个项目出列,StrongBox就会清除它的内部内容 - 因此GC可以收集StrongBox持有的T实例,而
Queue<T>
最终只持有StrongBox的一个实例(而不是 T 的实例)。


0
投票

使用 IEnumerator 是一个错误。该代码应该使用 ChildSyntaxList.Enumerator,它是一个结构。 StrongBox 的使用是为了性能,以避免在枚举器发生变化时需要从堆栈末尾推送和弹出枚举器。

© www.soinside.com 2019 - 2024. All rights reserved.