在檔案系統操作中,遞歸遍歷目錄樹是常見需求。本文提供一個可直接使用的 DirList 類別,支援指定起始路徑、選擇是否遞歸子目錄,並回傳 ArrayList 包含所有檔案完整路徑(空目錄以 /// 前綴標記)。
using System;
using System.IO;
using System.Collections;
namespace DSclub
{
///
/// 目錄遞歸列舉工具類別
///
public class DirList
{
private string _initPath;
private bool _recursive;
///
/// 預設建構子:從 C:\ 開始,不遞歸
///
public DirList()
{
_initPath = @"C:\";
_recursive = false;
}
///
/// 指定起始路徑建構子
///
public DirList(string startPath)
{
_initPath = startPath;
_recursive = false;
}
///
/// 是否遞歸子目錄
///
public bool Recursive
{
get => _recursive;
set => _recursive = value;
}
///
/// 取得檔案清單(使用建構子指定的路徑與模式)
///
public ArrayList GetFiles() => GetFiles(_initPath, _recursive);
///
/// 靜態方法:取得指定路徑的檔案清單
///
/// 起始目錄
/// 是否遞歸子目錄
/// 檔案路徑集合,空目錄以 "///" 開頭標記
public static ArrayList GetFiles(string path, bool recursive)
{
ArrayList result = new ArrayList();
// 驗證路徑存在
if (!Directory.Exists(path))
throw new ApplicationException($"路徑不存在或非目錄: {path}");
// 取得目前目錄下所有檔案
string[] files = Directory.GetFiles(path);
foreach (string file in files)
result.Add(file);
// 空目錄標記
if (files.Length == 0)
result.Add("///" + path);
// 遞歸處理子目錄
if (recursive)
{
string[] dirs = Directory.GetDirectories(path);
foreach (string dir in dirs)
result.AddRange(GetFiles(dir, true));
}
return result;
}
}
}
// 範例 1:列出 D:\Projects 所有檔案(含子目錄)
DirList lister = new DirList(@"D:\Projects");
lister.Recursive = true;
ArrayList allFiles = lister.GetFiles();
foreach (string f in allFiles)
Console.WriteLine(f);
// 範例 2:靜態方法直接呼叫(僅當前目錄)
ArrayList currentOnly = DirList.GetFiles(@"C:\Temp", false);
D:\Projects\A\test.cs///D:\Projects\EmptyFolder(方便識別無檔案資料夾)| 限制 | 改進建議 |
|---|---|
未處理 UnauthorizedAccessException(系統資料夾無權限) | 加入 try-catch 略過無權限目錄,或記錄錯誤日誌 |
| 未處理重新解析點/連結點(Junction/Symlink)導致無限遞歸 | 維護 HashSet<string> 記錄已訪問實體路徑 |
使用 ArrayList 非泛型集合 | 改為 List<string> 提供型別安全與效能 |
| 大量檔案記憶體壓力大 | 改為 IEnumerable<string> 配合 yield return 串流輸出 |
| 無檔案過濾功能 | 加入 searchPattern 參數支援萬用字元 |
using System;
using System.Collections.Generic;
using System.IO;
public static class DirectoryExtensions
{
///
/// 遞歸列舉檔案(串流輸出,記憶體友善)
///
public static IEnumerable EnumerateFilesRecursive(
string rootPath, string searchPattern = "*",
SearchOption option = SearchOption.AllDirectories)
{
if (!Directory.Exists(rootPath))
throw new DirectoryNotFoundException(rootPath);
var visited = new HashSet(StringComparer.OrdinalIgnoreCase);
var stack = new Stack();
stack.Push(Path.GetFullPath(rootPath));
while (stack.Count > 0)
{
string current = stack.Pop();
string realPath = Path.GetFullPath(current);
if (!visited.Add(realPath)) continue; // 防止符號連結循環
try
{
foreach (string file in Directory.EnumerateFiles(current, searchPattern))
yield return file;
if (option == SearchOption.AllDirectories)
{
foreach (string dir in Directory.EnumerateDirectories(current))
stack.Push(dir);
}
}
catch (UnauthorizedAccessException) { /* 忽略無權限目錄 */ }
catch (IOException) { /* 忽略 I/O 錯誤 */ }
}
}
}
foreach (string file in DirectoryExtensions.EnumerateFilesRecursive(@"D:\Projects", "*.cs"))
Console.WriteLine(file);
// 只列出當前目錄
foreach (string file in Directory.EnumerateFiles(@"D:\Projects", "*.cs", SearchOption.TopDirectoryOnly))
Console.WriteLine(file);