2

我想尝试制作自己的文件资源管理器。我有一个算法来枚举所有驱动器中的所有目录。但它运行得太慢了。这是我的代码:

public ExplorerForm()
{
    InitializeComponent();
    this.SuspendLayout();//Without this, it will be far more slow again!
    string[] a = System.IO.Directory.GetLogicalDrives();// a is used for drive array
    for(int b = 0; b < a.Length; b++)//B is an enumerator. Ussually it is only one letter
    {
        //Defining the node
        TreeNode c = new TreeNode();//c i place for TreeNode
        c.Text = a[b].Substring(0,2);
        c.Tag = a[b];
        ApplyNodes(a[b], ref c);
        if(c != null) tv.Nodes.Add(a)
    }
    this.ResumeLayout(false);
}
private void ApplyNodes(string a, ref TreeNode b)//a=directory, b applied TreeNode
{
    try{
        List<string> c = new List<string>(Directory.EnumerateDirectories(a);//c = directories
        if (c.Count == 0 ) return;
        for(int d = 0; d < c.Count; d++)//d = enumerator.
        {
            TreeNode e = new TreeNode();//e = TreeNode
            var z = c[b].Split(Convert.ToChar("/"));
            e.Text = z[z.Length-1]
            e.Tag = c[b];
            ApplyNodes(c[b], e)
            if(e != null) b.Nodes.Add(e)
        }
    }catch (UnauthorizedAccessException){
    }catch (IOException){  //For test, It is removed. and my E: is not ready
    }
}

电视是我的控制。它运行得很慢。当我删除选择的行时会显示,抛出 IOException 需要 10 多秒的时间。帮助我如何改进枚举。使用线程和部分更新除外。如果以后不能修复,请告诉我原因。

4

2 回答 2

5

除了枚举系统上所有目录所花费的时间(如果您实施延迟加载,您可能会看到更好的性能),将项目插入 aTreeView可能需要大量时间。

来自TreeView.BeginUpdate

要在将项目一次添加到 TreeView 时保持性能,请调用 BeginUpdate 方法。BeginUpdate 方法阻止控件绘制,直到调用 EndUpdate 方法。将项添加到树视图控件的首选方法是使用 AddRange 方法将树节点项数组添加到树视图。

...

要允许控件继续绘制,请在所有树节点都已添加到树视图时调用 EndUpdate 方法。

尽管它与 .NET 不同,但 Raymond Chen 的博客文章如何有效地将大量项目插入树视图中包含更多信息,这些信息可能会帮助您以一种导致更好的项目插入性能的方式构建代码。

如果您需要将大量项目(如数万个)插入到树视图中,那么“向后”插入它们会更有效。

编辑

这是一个将目录枚举放到线程上的示例。观察TreeView控件的可用性(或缺乏)。如果不出意外,这可能是使用延迟加载的最佳论据。

private void Form1_Load(object sender, EventArgs e)
{
    var treeNode = new TreeNode("Sea Drive");
    treeView1.Nodes.Add(treeNode);

    ThreadPool.QueueUserWorkItem(_ => TraverseDirectory("C:\\", treeNode));
}
   
private static readonly string DirectorySeparatorString = Path.DirectorySeparatorChar.ToString();

private void TraverseDirectory(string initialDirectoryPath, TreeNode initialTreeNode)
{
    var initialTuples = new[] {Tuple.Create(initialDirectoryPath, initialTreeNode)};
    var directoryQueue = new Queue<Tuple<string, TreeNode>>(initialTuples);

    while (directoryQueue.Any())
    {
        var tuple = directoryQueue.Dequeue();
        var parentDirectoryPath = tuple.Item1;
        var parentTreeNode = tuple.Item2;

        try
        {
            var treeNodes = new List<TreeNode>();
            var directories = Directory.EnumerateDirectories(parentDirectoryPath);

            foreach (var directoryPath in directories)
            {
                var lastDirectorySeparator = directoryPath.LastIndexOf(DirectorySeparatorString);
                var directoryName = directoryPath.Substring(lastDirectorySeparator + 1);

                // Add the tree node to our list of child 
                // nodes, for an eventual call to AddRange
                var treeNode = new TreeNode(directoryName);
                treeNodes.Add(treeNode);

                // We have to go deeper
                directoryQueue.Enqueue(Tuple.Create(directoryPath, treeNode));
            }

            // Run this operation on the main thread
            Invoke((Action)(() => parentTreeNode.Nodes.AddRange(treeNodes.ToArray())));
        }
        catch (Exception exception)
        {
            Trace.Write(exception);
        }
    }
}

示例不完整;您需要提供自己的Form控制TreeView权。

于 2013-07-07T04:57:35.843 回答
5

除了关于改进 TreeView 人口调用的先前答案之外,您还应该阅读 MSDN 页面How to: Enumerate Directories and Files

第一段提到了一些性能改进(使用 DirectoryInfo 的可枚举集合而不是字符串) - 请注意最后一行:

您可以使用返回名称字符串的可枚举集合的方法来枚举目录和文件。您还可以使用返回 DirectoryInfo、FileInfo 或 FileSystemInfo 对象的可枚举集合的方法。当您处理大量目录和文件的集合时,可枚举集合提供比数组更好的性能。

然而,即使有了这个改进,你真的不应该递归下降 ApplyNodes 内的整个子树。只需读取一个级别,为您的当前节点添加条目,以大大减少您需要遍历的子目录的数量(这肯定是文件资源管理器所做的事情。)这就是提到的“延迟加载”技术的重点以上来自 ta.speot.is

如果这两个改进仍然不能给你想要的性能,那么你可能想要增加更多的复杂性(例如运行一个后台线程来执行你的遍历),但是你需要对到底是哪一部分有一个很好的了解您的代码首先是您的瓶颈(这意味着您需要添加计时代码和日志记录)

于 2013-07-07T08:21:32.690 回答