我正在查看带有Reflector的Roslyn September 2012 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<>
(通常是值类型)涉及哪种性能和安全权衡?
答案 0 :(得分:3)
CommonSyntaxNode
是一个抽象类,它包含很多值类型,可以继承到一个大对象中。
IEnumerator<CommonSyntaxNode>
仅包含对CommonSyntaxNode
的引用,因此CommonSyntaxNode
的大小似乎不会影响Enumerator
大小,因为它只是一个参考,< EM>但:
因为 IEnumerator<T>
的方法 MoveNext()
使用yield return;
Enumerator
中的每次迭代都会导致该方法保存它的状态,直到下一次迭代。
因为整个方法状态足够重,并且它可能包含CommonSyntaxNode
的属性以执行 MoveNext()
逻辑,而不是整个IEnumerator<CommonSyntaxNode>
记忆可能会非常沉重。
使用 StrongBox<>
会导致Queue
或Stack
只保留一个小的引用对象(StrongBox<>
而不是内存中可能很重的{ {1}} - 因此 - GC正在更快地清除内存中包含IEnumerator<CommonSyntaxNode>
或Queue
的 Stack
或IEnumerator<CommonSyntaxNode>
- 减少应用程序的总内存消耗。
请注意CommonSyntaxNode
的枚举器是一个结构,使用它直接意味着深度复制整个结构,它是一个小结构,所以它不是很重,但仍然...
答案 1 :(得分:3)
StrongBox<T>
的优点是,一旦项目出列,StrongBox就会清除它的内部内容 - 因此GC可以收集StrongBox持有的T实例,Queue<T>
结束只持有StrongBox的一个实例(而不是T的实例)。
答案 2 :(得分:0)
使用IEnumerator是一个错误。代码应该使用ChildSyntaxList.Enumerator,它是一个结构。 StrongBox的使用是针对性能,以防止需要推送和当它们发生变化时,从堆栈末尾弹出枚举器。