简体   繁体   English

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

[英]How to ignore an exception and continue processing a foreach loop?

I have a test program that is supposed to loop over all the files under C:. 我有一个测试程序,它应该遍历C:下的所有文件。 It dies when it hits the "Documents and Settings" folder. 当它到达“Documents and Settings”文件夹时它会死掉。 I'd like to ignore the error and keep looping over the remaining files. 我想忽略错误并继续循环其余文件。 The problem is that the exception is thrown in the foreach , so putting a try/catch around the foreach will cause the loop to exit. 问题是,异常抛出foreach ,所以把一个try/catch周围foreach会导致循环退出。 And putting a try/catch after the foreach never fires (because the exception is thrown in the foreach ). 并把一个try/catchforeach永远不会触发(因为异常抛出foreach )。 Is there any way to ignore the exception and continue processing the loop? 有没有办法忽略异常并继续处理循环?

Here's the code: 这是代码:

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

The problem is that IEnumerable<string> behaves lazily, (kind of like a stream). 问题是IEnumerable<string>表现得懒散,(有点像流)。 foreach takes one string after another from files , so only when it requests the problem directory, the enumerator crashes. foreachfiles接受一个接一个的字符串,因此只有当它请求问题目录时,枚举器才会崩溃。 (you can confirm this by calling ToList() or something like this: (您可以通过调用ToList()或类似的东西来确认:

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

I think you need to find a way to convert files into an enumerated collection, by manually getting each item from files and do a try/catch around the actual read from the enumerator. 我认为您需要找到一种方法将files转换为枚举集合,方法是从files手动获取每个项目,并对枚举器的实际读取执行try / catch。

edit: chris and servy has good points in comments. 编辑: chris和servy在评论中有很好的分数。 You probably shouldn't mess with the enumerator at all after it throws an exception. 在抛出异常之后,你可能根本不应该弄乱枚举器。 Try to be more conservative in your query of files would probably be the easiest solution. 尝试在文件查询中更加保守可能是最简单的解决方案。

There is no effective way for you to handle this case. 没有有效的方法来处理这种情况。 If the iterator itself is throwing an exception when trying to get the next item then it is not going to be in a position to give you the item after that; 如果迭代器本身在尝试获取下一个项目时抛出异常,那么它就不会在此之后为您提供该项目; it is no longer in a "valid" state. 它不再处于“有效”状态。 Once an exception is thrown you're done with that iterator. 一旦抛出异常,你就完成了迭代器。

Your only option here is to not query the entire drive using this method. 这里唯一的选择是不使用此方法查询整个驱动器。 Perhaps you could write your own iterator to traverse the drive manually in such a way that items that cannot be traversed are skipped more gracefully. 也许您可以编写自己的迭代器来手动遍历驱动器,以便更优雅地跳过无法遍历的项目。

So, to do this manual traversal. 所以,要做这个手动遍历。 We can start out with a generalize Traverse method that can traverse any tree (non-recursively, to better handle deep stacks efficiently): 我们可以从一个可以遍历任何树的通用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);
    }
}

Now we just grab the root, traverse it, and use a method of getting the sub directories that won't throw an exception: 现在我们只需获取根,遍历它,并使用获取不会引发异常的子目录的方法:

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

The method to get the sub directories can start out as something like this, but may need to be fleshed out: 获取子目录的方法可以像这样开始,但可能需要充实:

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>();
    }
}

Note that you can play around with this particular part a bit. 请注意,您可以稍微使用此特定部分。 You may be able to get some of the sub directories out here even if you can't get others, you'll likely need to adjust the error handling, etc. The get files version will be essentially the same, but just with GetFiles instead. 即使您无法获得其他子目录,您也可以在这里获得一些子目录,您可能需要调整错误处理等。获取文件版本基本相同,但只需使用GetFiles

You are probably getting an UnauthorizedAccessException because some files are hidden or a system file.Since you are working with strings(from enumeratefiles)and not fileinfos or directoryinfo objects i rewrote this to fit your design: 您可能会收到UnauthorizedAccessException,因为某些文件是隐藏的或系统文件。因为您正在处理字符串(来自enumeratefiles)而不是fileinfos或directoryinfo对象,所以我重写了这个以适合您的设计:

first add these 2 member variables: 首先添加这两个成员变量:

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

then place this method: 然后放置这个方法:

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);
          }
     }
}

Then in Main you call it like so: 然后在Main中你这样称呼它:

RecursiveSearch(@"c:\");

After your DirectoryTree dictionary and list logger will be filled. 在您的DirectoryTree字典和列表记录器将被填充后。

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)
        {
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM