[英]How to ignore an exception and continue processing a foreach loop?
我有一個測試程序,它應該遍歷C:下的所有文件。 當它到達“Documents and Settings”文件夾時它會死掉。 我想忽略錯誤並繼續循環其余文件。 問題是,異常在拋出foreach
,所以把一個try/catch
周圍foreach
會導致循環退出。 並把一個try/catch
后foreach
永遠不會觸發(因為異常的拋出foreach
)。 有沒有辦法忽略異常並繼續處理循環?
這是代碼:
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);
}
問題是IEnumerable<string>
表現得懶散,(有點像流)。 foreach
從files
接受一個接一個的字符串,因此只有當它請求問題目錄時,枚舉器才會崩潰。 (您可以通過調用ToList()
或類似的東西來確認:
//should now crash here instead
List<string> files = Directory.EnumerateFiles(@"C:\", "*.*",
SearchOption.AllDirectories).ToList();
我認為您需要找到一種方法將files
轉換為枚舉集合,方法是從files
手動獲取每個項目,並對枚舉器的實際讀取執行try / catch。
編輯: chris和servy在評論中有很好的分數。 在拋出異常之后,你可能根本不應該弄亂枚舉器。 嘗試在文件查詢中更加保守可能是最簡單的解決方案。
沒有有效的方法來處理這種情況。 如果迭代器本身在嘗試獲取下一個項目時拋出異常,那么它就不會在此之后為您提供該項目; 它不再處於“有效”狀態。 一旦拋出異常,你就完成了迭代器。
這里唯一的選擇是不使用此方法查詢整個驅動器。 也許您可以編寫自己的迭代器來手動遍歷驅動器,以便更優雅地跳過無法遍歷的項目。
所以,要做這個手動遍歷。 我們可以從一個可以遍歷任何樹的通用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);
}
}
現在我們只需獲取根,遍歷它,並使用獲取不會引發異常的子目錄的方法:
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>();
}
}
請注意,您可以稍微使用此特定部分。 即使您無法獲得其他子目錄,您也可以在這里獲得一些子目錄,您可能需要調整錯誤處理等。獲取文件版本基本相同,但只需使用GetFiles
。
您可能會收到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字典和列表記錄器將被填充后。
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.