在" postorder"中迭代地枚举目录

时间:2015-12-23 17:04:04

标签: c# directory ienumerable

假设我想枚举一个给定目录的文件&目录迭代,以这种方式首先发出内部目录和文件。

如果要在foreach循环中使用函数执行此枚举:DeleteFile和DeleteEmptyDirectory,它不应该失败,因为最内部的项目首先被放弃。

现在当然可以做到这一点(伪代码):

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;

但是这会在调查员身上浪费大量的分配。

也可以使用两个堆栈"获取"将所有目录放在一起,然后只是不断弹出项目,但这会浪费太多内存,并且不能很好地平衡MoveNext时间。

关于如何有效地做到这一点而不浪费太多空间的任何想法?

1 个答案:

答案 0 :(得分:1)

本着DRY原则的精神,我会使用我对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();
        }
    }
}

在你的情况下使用它会是这样的

var directory = new DirectoryInfo(path);
var result = TreeHelper.Traverse<FileSystemInfo>(directory, fsi => 
    fsi is DirectoryInfo ? ((DirectoryInfo)fsi).EnumerateFileSystemInfos() : null,
    preOrder: false)