【问题标题】:How to access DirectoryInfo.EnumerateFiles before it has completed如何在完成之前访问 DirectoryInfo.EnumerateFiles
【发布时间】:2012-05-15 17:11:41
【问题描述】:

在我问过Retrieve a list of filenames in folder and all subfolders quickly 和我发现的其他几个问题中,似乎搜索许多文件的方法是使用 EnumerateFiles 方法。

EnumerateFiles 和 GetFiles 方法的区别如下: 使用 EnumerateFiles,就可以开始枚举名字的集合了 在返回整个集合之前;当您使用 GetFiles 时,您 必须等待返回整个名称数组,然后才能 访问数组。因此,当您处理许多文件和 目录,EnumerateFiles 可以更高效。

这对我来说听起来很棒,我的搜索大约需要 10 秒,所以我可以在信息进入时开始制作我的列表。但我无法弄清楚。当我运行 EnumerateFiles 方法时,应用程序会冻结,直到它完成。我可以在后台工作人员中运行它,但同样的事情也会发生在那个线程上。有什么帮助吗?

 DirectoryInfo dir = new DirectoryInfo(MainFolder);
 List<FileInfo> matches = new List<FileInfo>(dir.EnumerateFiles("*.docx",SearchOption.AllDirectories));

//This wont fire until after the entire collection is complete
DoSoemthingWhileWaiting();

【问题讨论】:

    标签: c# winforms io .net


    【解决方案1】:

    您可以通过将其推送到后台任务中来做到这一点。

    例如,你可以这样做:

    var fileTask = Task.Factory.StartNew( () =>
    {
        DirectoryInfo dir = new DirectoryInfo(MainFolder);
        return new List<FileInfo>(
               dir.EnumerateFiles("*.docx",SearchOption.AllDirectories)
               .Take(200) // In previous question, you mentioned only wanting 200 items
           );
    };
    
    // To process items:
    fileTask.ContinueWith( t =>
    {
         List<FileInfo> files = t.Result;
    
         // Use the results...
         foreach(var file in files)
         {
             this.listBox.Add(file); // Whatever you want here...
         }
    }, TaskScheduler.FromCurrentSynchronizationContext()); // Make sure this runs on the UI thread
    
    DoSomethingWhileWaiting();
    

    您在评论中提到:

    我想在列表中显示它们。并在它们进入时完美地将它们发送到主用户界面

    在这种情况下,您必须在后台处理它们,并在它们进入时将它们添加到列表中。类似于:

    Task.Factory.StartNew( () =>
    {
        DirectoryInfo dir = new DirectoryInfo(MainFolder);
        foreach(var tmp in dir.EnumerateFiles("*.docx",SearchOption.AllDirectories).Take(200))
        {
            string file = tmp; // Handle closure issue
    
            // You may want to do this in batches of >1 item...
            this.BeginInvoke( new Action(() =>
            {
                 this.listBox.Add(file);
            }));
        }
    });
    DoSomethingWhileWaiting();
    

    【讨论】:

    • 感谢里德迟到。我本可以在 5 分钟前使用它。这几乎正​​是我想要的,但我已经从我过去的问题和上面的答案中弄清楚了。非常感谢您花时间提供如此完整的答案。
    • @K'Leg Faster != Better ;) 认为我会提供您可能需要的一切,包括这里的多个选项...
    • Reed 看来我们在您的第二个答案中缺少一个右括号。我尝试将其添加到末尾,但出现错误。有什么想法吗?
    • @K'Leg 缺少括号 - 已添加。应该 });在 StartNew 结束时
    • 谢谢,我刚刚想通了。我把它放在 };) 后面,而它应该放在中间 });
    【解决方案2】:

    应用程序冻结,因为您通过将它放在List&lt;FileInfo&gt; 的构造函数中来使用这个枚举。就好像您急切地调用旧方法一样。因此,正确的方法是在后台线程中运行它,然后不要立即将结果传递给将消耗可枚举直到结束的东西,而是在它们到达循环时开始迭代并添加项目。

    我可以在后台工作程序中运行它,但同样的事情也会发生在那个线程上。

    是的,在循环期间,您显然会冻结后台线程,但这就是后台线程的意义所在:避免冻结主 UI 线程。只需确保当您将项目传递给主线程以显示它们时,您正在使用与 UI 的正确同步。在 WinForms 中,Control.Invoke 方法会发生这种情况。当然要小心,因为在后台线程和 UI 线程之间经常编组也会产生负面影响,让您感觉您的应用程序冻结。要解决此问题,您可以在文件以块的形式到达时传递它们。

    这是一个例子:

    Task.Factory.StartNew(() =>
    {
        var dir = new DirectoryInfo(MainFolder);
        var files = dir.EnumerateFiles("*.docx", SearchOption.AllDirectories);
        foreach (var file in files)
        {
            Action<string> del = f => listBox1.Items.Add((string)f);
            BeginInvoke(del, file);
        }
    });
    

    【讨论】:

    • 如果我不能把它变成一个列表,我该如何存储和访问它?抱歉,我还没收到?
    • 你想对这些结果做什么?在 UI 的某个地方显示它们?在 ListBox 还是什么的?您不应该将它们存储在列表中。您应该将结果直接编组到主 UI 线程,以便在它们到达时显示。所以这真的取决于你需要对结果做什么。
    • 是的,我想在列表中显示它们。并完美地将它们发送到主 ui。我缺少的是,如果后台工作人员在处理此问题时冻结,我该如何将它们发送到主 ui?
    • 在块中,使用 Control.Invoke 方法。或者您可以将它们存储在一个列表中,一旦枚举完成,您可以使用 Control.Invoke 将此列表绑定到主 UI 控件。但在这种情况下,商品到达时不会出现。
    • 感谢达林 Gabba 的回答中的 foreach 我认为促使我理解。我很感谢你的帮助
    【解决方案3】:

    你可以的

        forach(var f in dir.EnumerateFiles("*.docx",SearchOption.AllDirectories))
        {
    //.. process file
        }
    

    在线程中,并在进程完成后触发一个事件

    【讨论】:

    • 所以如果我立即运行它,然后在 5 秒后再次运行它,它们可能会得到不同的结果?
    • 我的意思是第一个文件将在所有枚举完成之前出现。如果要处理文件系统更改,可以使用 filesystemwatcher:msdn.microsoft.com/en-us/library/…
    • 哦,等等,我想我明白了。如果我每次找到结果时都运行它,它将触发 foreach 的另一次迭代...在 foreach 内我可以将更新从后台工作人员发送到 ui...这是正确的吗?
    • 可以,但不要忘记与 UI 同步
    • Reed Copsey 的第二个答案看起来很完美
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2022-12-31
    • 1970-01-01
    • 1970-01-01
    • 2016-09-16
    相关资源
    最近更新 更多