问题可以分解为几个部分:
(1) 递归枚举所有可访问目录
(2) 枚举多个目录文件
(3) 获取不同的文件扩展名
请注意,只有 (3) 是特定的,(1) 和 (2) 是通用的,可用于其他处理(如您的 SearchAccessibleFiles 等)。所以让我们分别解决它们:
(1)递归枚举所有可访问目录:
这又可以分为两部分:
(A) 递归枚举通用树结构
(B) 可访问目录树的上述特化
对于 (A) 我个人使用我对 How to flatten tree via LINQ? 的回答中的帮助方法和类似方法:
public static class TreeUtils
{
public static IEnumerable<T> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
{
var stack = new Stack<IEnumerator<T>>();
var e = source.GetEnumerator();
try
{
while (true)
{
while (e.MoveNext())
{
var item = e.Current;
yield return item;
var elements = elementSelector(item);
if (elements == null) continue;
stack.Push(e);
e = elements.GetEnumerator();
}
if (stack.Count == 0) break;
e.Dispose();
e = stack.Pop();
}
}
finally
{
e.Dispose();
while (stack.Count != 0) stack.Pop().Dispose();
}
}
}
这是我们案例的专业化:
public static partial class DirectoryUtils
{
public static IEnumerable<DirectoryInfo> EnumerateAccessibleDirectories(string path, bool all = false)
{
var filter = Func((IEnumerable<DirectoryInfo> source) =>
source.Select(di =>
{
try { return new { Info = di, Children = di.EnumerateDirectories() }; }
catch (UnauthorizedAccessException) { return null; }
})
.Where(e => e != null));
var items = filter(Enumerable.Repeat(new DirectoryInfo(path), 1));
if (all)
items = items.Expand(e => filter(e.Children));
else
items = items.Concat(items.SelectMany(e => filter(e.Children)));
return items.Select(e => e.Info);
}
static Func<T, TResult> Func<T, TResult>(Func<T, TResult> func) { return func; }
}
(2)枚举多个目录文件:
消除重复代码的简单扩展方法:
partial class DirectoryUtils
{
public static IEnumerable<FileInfo> EnumerateAccessibleFiles(string path, bool allDirectories = false)
{
return EnumerateAccessibleDirectories(path, allDirectories).EnumerateFiles();
}
public static IEnumerable<FileInfo> EnumerateFiles(this IEnumerable<DirectoryInfo> source)
{
return source.SelectMany(di => di.EnumerateFiles());
}
}
(3) 获取不同的文件扩展名:
使用上述帮助程序,这是一个简单的 LINQ 查询:
var result = DirectoryUtils.EnumerateAccessibleFiles(rootPath, true)
.Select(file => file.Extension).Distinct()
.ToList();
最后,为了比较,以下是使用相同帮助器时原始方法的外观:
IEnumerable<string> SearchAccessibleFiles(string root, string searchTerm)
{
return DirectoryUtils.EnumerateAccessibleFiles(rootPath, true)
.Where(file => file.FullName.Contains(searchTerm))
.Select(file => file.FullName);
}
如果搜索词不应该包含目录信息,您可以将过滤条件更改为file.Name.Contains(searchTerm)。