评估递归搜索的进度

j0h4nn3s

我有一个递归搜索,该搜索在整个目录中搜索具有指定名称的文件。这需要花费一些时间,我想显示一个进度条,其中显示至少一些进度(我不在乎进度是仅由文件或目录的数量还是它们的实际大小来衡量的)。

以下(递归代码)执行搜索:

private void searchFolder(PortableDeviceFolder parent, ref List<PortableDeviceFolder> result, string filename)
{
    foreach (var item in parent.Files)
    {

        if (item is PortableDeviceFolder)
        {
            searchFolder((PortableDeviceFolder)item, ref result, filenames);
        }
        else if ((String.Compare(item.Name, filename)==0))
        {
            result.Add(parent);
            Console.WriteLine("Found file in: " + parent.Name);
        }    
    }
}

我不知道如何取得进展。我在考虑一种简单的算法,该算法对每个文件和文件夹测量相同的百分比;

  • 文件(10%)
    • 私人(10%/ 3)
    • 工作(10%/ 3)
      • 专案1(10%/ 3/2)
      • 专案2(10%/ 3/2)
    • 家庭(10%/ 3)
  • ...

等等。

不幸的是,我无法在递归搜索中实现这一点。有人有时间给我一个例子吗?提前致谢

彼得·杜尼奥(Peter Duniho)

评论员TaW的建议是,可以将目录用作进度的基础,这很好。但是有一个小问题,不是建议本身,而是代码的实现方式。

从您的问题尚不清楚PortableDeviceFolder什么但是它似乎抽象了文件和文件夹之间的差异,并从Files属性返回两者鉴于处理过程中最耗时的方面可能是实际检索给定目录的文件名,因此,如果唯一的机制PortableDeviceFolder必须返回给定目录中的目录是该Files属性,则必须结束枚举所有文件以及目录,而忽略为生成计数而返回的文件。

换句话说,获取计数几乎需要花费与实际搜索给定名称所需的时间相同的时间。

因此,对于这个答案的目的,我将假设PortableDeviceFolder类有另一个属性Folders,它返回刚才的目录本身。有了这样的属性,我们就可以利用TaW提供的建议。

首先,您需要获得该计数。这样做的方法如下所示:

private int CountFolders(PortableDeviceFolder rootFolder)
{
    return rootFolder.Folders.Select(folder => CountFolders(folder)).Sum() + 1;
}

在显示任何进度之前会稍有延迟,因为当然没有一种有用的方法可以预测上述过程需要多长时间。但这应该是相对简短的,因为我们只查看目录,而不是所有文件。

为了能够在处理进行时更新进度,我们应该在单独的线程中运行搜索并用于Dispatcher.Invoke()更新ProgressBar.Value属性。搜索方法如下所示:

private void SearchFolder(PortableDeviceFolder parent,
    List<PortableDeviceFolder> result, string fileName, IProgress<int> progress)
{
    foreach (var item in parent.Files)
    {
        PortableDeviceFolder folder = item as PortableDeviceFolder;

        if (folder != null)
        {
            SearchFolder(folder, result, fileName, progress);
        }
        else if (item.Name.Equals(fileName, StringComparison.OrdinalIgnoreCase))
        {
            result.Add(parent);
        }
    }

    progress.Report(1);
}

注意:我不清楚您为什么要按result引用传递参数。似乎并不需要,因此我将其更改为常规的按值参数。

在您的UI代码中,您可以这样称呼它:

private async void SearchFolder_Click(object sender, RoutedEventArgs e)
{
    Button button = (Button)sender;

    button.IsEnabled = false;

    string searchPath = textBlock1.Text, searchText = textBox1.Text;
    List<PortableDeviceFolder> folders = new List<PortableDeviceFolder>();
    PortableDeviceFolder rootFolder = new WindowsDirectoryFolder(searchPath);

    progressBar1.Value = 0;
    progressBar1.Maximum = await Task.Run(() => CountFolders(rootFolder));

    Progress<int> progress =
        new Progress<int>(increment => progressBar1.Value += increment);

    await Task.Run(() => SearchFolder(rootFolder, folders, searchText, progress));

    listBox1.ItemsSource = folders;
    button.IsEnabled = true;
}

请注意,这里我们异步地执行CountFolders()SearchFolder()方法,以确保在工作完成时UI保持响应。

上面是我编写的一个简单的WPF程序来演示该技术,但是可以轻松地将其应用于Winforms或其他GUI框架。基本思想保持不变。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章