遞歸雖然代碼簡潔,但存在顯著缺陷:調用棧深度不可控,易導致 StackOverflowError;調用鏈未結束時,棧幀內的變量與引用無法釋放,造成內存壓力與 CPU 開銷。對於深度未知的目錄樹,改用顯式棧(深度優先)或隊列(廣度優先)的迭代寫法,能徹底規避上述風險。
private static void RecursiveTraverse(string path, ref List<string> files) {
// 獲取文件
string[] fileEntries = Directory.GetFiles(path);
if (fileEntries.Length > 0) files.AddRange(fileEntries);
// 遞歸子目錄
string[] subDirs = Directory.GetDirectories(path);
foreach (var dir in subDirs) RecursiveTraverse(dir, ref files);
}
缺點:深度過大時棧溢出;異常處理分散。
private static void StackTraverse(string root, ref List<string> files) {
var stack = new Stack<string>();
stack.Push(root);
while (stack.Count > 0) {
string dir = stack.Pop();
// 處理文件
files.AddRange(Directory.GetFiles(dir));
// 子目錄入棧(逆序入棧可保持原序)
foreach (var sub in Directory.GetDirectories(dir).Reverse())
stack.Push(sub);
}
}
優點:可控內存,無棧溢出風險;易於加入中斷/進度回調。
private static void QueueTraverse(string root, ref List<string> files) {
var queue = new Queue<string>();
queue.Enqueue(root);
while (queue.Count > 0) {
string dir = queue.Dequeue();
files.AddRange(Directory.GetFiles(dir));
foreach (var sub in Directory.GetDirectories(dir))
queue.Enqueue(sub);
}
}
優點:層級順序輸出,適合需按目錄深度處理的場景。
if (true) { ... } 的作用代碼中出現的 if (true) { ... } 看似多餘,實則是為了限制變量作用域(如 files、folders),讓編譯器更早回收記憶體,並在某些編譯器優化下生成更緊湊的 IL 代碼。
| 方式 | 內存佔用 | 順序 | 適用場景 |
|---|---|---|---|
| 遞歸 | 不可控(棧幀) | DFS | 淺層、確定深度 |
| 顯式棧 | 可控(堆上) | DFS | 深層、需回溯邏輯 |
| 隊列 | 可控(堆上) | BFS | 分層處理、最短路徑 |
實戰建議:默認優先選擇顯式棧(DFS),兼顧性能與代碼簡潔;僅在需要層級遍歷時改用隊列。