问题
Suppose I wanted to enumerate through a given directory's files & directories iteratively, in such a way that the inner directories and files are given out first.
If you were to execute this enumerable in a foreach loop with the functions: DeleteFile and DeleteEmptyDirectory it should not fail, because the most inner items are yielded out first.
Now of course one could do this (pseudocode):
func(Directory dir)
foreach (var f in dir.EnumerateFileSystemInfos())
if (f is FileInfo)
yield return f;
else
foreach (var item in func(f))
yield return item;
yield return dir;
But that has wastes a lot of allocations on enumerators.
One could also use two stacks and "acquire" all of the directories together and then just keep popping out items but that wastes too much memory and doesn't balance the MoveNext times that well.
Any ideas on how to do this efficiently without wasting too much space?
回答1:
In the spirit of DRY principle, I would use the function from my answer to How to make an IEnumerable of values of tree nodes?
public static class TreeHelper
{
public static IEnumerable<T> Traverse<T>(T node, Func<T, IEnumerable<T>> childrenSelector, bool preOrder = true)
{
var stack = new Stack<IEnumerator<T>>();
var e = Enumerable.Repeat(node, 1).GetEnumerator();
try
{
while (true)
{
while (e.MoveNext())
{
var item = e.Current;
var children = childrenSelector(item);
if (children == null)
yield return item;
else
{
if (preOrder) yield return item;
stack.Push(e);
e = children.GetEnumerator();
}
}
if (stack.Count == 0) break;
e.Dispose();
e = stack.Pop();
if (!preOrder) yield return e.Current;
}
}
finally
{
e.Dispose();
while (stack.Count != 0) stack.Pop().Dispose();
}
}
}
Utilizing it for your case would be something like this
var directory = new DirectoryInfo(path);
var result = TreeHelper.Traverse<FileSystemInfo>(directory, fsi =>
fsi is DirectoryInfo ? ((DirectoryInfo)fsi).EnumerateFileSystemInfos() : null,
preOrder: false)
来源:https://stackoverflow.com/questions/34440512/enumerating-directories-iteratively-in-postorder