如何忽略异常并继续处理foreach循环?

时间:2013-10-24 15:47:28

标签: c# .net foreach

我有一个测试程序,它应该遍历C:下的所有文件。当它到达“Documents and Settings”文件夹时它会死掉。我想忽略错误并继续循环其余文件。问题是该异常在<{1}} 中抛出,因此在foreach周围放置try/catch将导致循环退出。并且在foreach之后放置try/catch永远不会触发(因为异常被抛出 foreach)。有没有办法忽略异常并继续处理循环?

以下是代码:

foreach

4 个答案:

答案 0 :(得分:3)

问题在于IEnumerable<string>懒惰,(有点像流)。 foreachfiles接受一个接一个的字符串,因此只有当它请求问题目录时,枚举器才会崩溃。 (您可以通过调用ToList()或类似的内容来确认:

//should now crash here instead
List<string> files = Directory.EnumerateFiles(@"C:\", "*.*",
                            SearchOption.AllDirectories).ToList();

我认为您需要找到一种方法将files转换为枚举集合,方法是从files手动获取每个项目,并对枚举器的实际读取执行try / catch。

编辑:chris和servy在评论中有很好的分数。在抛出异常之后,你可能根本不应该弄乱枚举器。尝试在文件查询中更加保守可能是最简单的解决方案。

答案 1 :(得分:3)

没有有效的方法来处理这种情况。如果迭代器本身在尝试获取下一个项目时抛出异常,那么它就不会在此之后为您提供该项目;它不再处于“有效”状态。一旦抛出异常,你就完成了迭代器。

这里唯一的选择是不使用此方法查询整个驱动器。也许您可以编写自己的迭代器来手动遍历驱动器,以便更优雅地跳过无法遍历的项目。

所以,要做这个手动遍历。我们可以从一个可以遍历任何树的泛化Traverse方法开始(非递归,以便更有效地处理深层堆栈):

public static IEnumerable<T> Traverse<T>(
    this IEnumerable<T> source
    , Func<T, IEnumerable<T>> childrenSelector)
{
    var stack = new Stack<T>(source);
    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childrenSelector(next))
            stack.Push(child);
    }
}

现在我们只需抓取root,遍历它,并使用获取不会引发异常的子目录的方法:

var root = new DirectoryInfo("C:\\");
var allFiles = new[] { root }.Traverse(dir => GetDirectoriesWithoutThrowing(dir))
    .SelectMany(dir => GetFilesWithoutThrowing(dir));

获取子目录的方法可以像这样开始,但可能需要充实:

private static IEnumerable<DirectoryInfo> GetDirectoriesWithoutThrowing(
    DirectoryInfo dir)
{
    try
    {
        return dir.GetDirectories();
    }
    catch (Exception)//if possible catch a more derived exception
    {
        //TODO consider logging the exception
        return Enumerable.Empty<DirectoryInfo>();
    }
}

请注意,您可以稍微使用此特定部分。即使您无法获得其他子目录,您也可以在这里获得一些子目录,您可能需要调整错误处理等。获取文件版本基本相同,但只是{{{ 1}}而不是。

答案 2 :(得分:1)

您可能会收到UnauthorizedAccessException,因为某些文件是隐藏的或系统文件。因为您正在使用字符串(来自enumeratefiles)而不是fileinfos或directoryinfo对象,所以我重写了这个以适合您的设计:

首先添加这两个成员变量:

private static Dictionary<DirectoryInfo, List<string>> DirectoryTree = new Dictionary<DirectoryInfo, List<string>>();
private static List<string> logger = new List<string>();

然后放置此方法:

static void RecursiveSearch(string root)
{
     string[] files = null;
     string[] subDirs = null;

     // First, process all the files directly under this folder 
     try
     {
        files = Directory.EnumerateFiles(root).ToArray();
     }
     catch (UnauthorizedAccessException e)
     {
         logger.Add(e.Message);
     }
     catch (System.IO.DirectoryNotFoundException e)
     {
         Console.WriteLine(e.Message);
     }

     if (files != null)
     {

          DirectoryTree.Add(new DirectoryInfo(root), files.ToList());
          subDirs = Directory.GetDirectories(root);

          foreach (string dir in subDirs)
          {
              RecursiveSearch(dir);
          }
     }
}

然后在Main中你这样称呼它:

RecursiveSearch(@"c:\");

将填写您的DirectoryTree词典和列表记录器。

答案 3 :(得分:-3)

static void Main(string[] args)
{
    IEnumerable<string> files = Directory.EnumerateFiles(@"C:\", "*.*",
                            SearchOption.AllDirectories);
    foreach (string file in files)  // Exception occurs when evaluating "file"
    {
        try
        {
            Console.WriteLine(file);
        }
        Catch(Exception ex)
        {
        }
    }
}