Проблема может быть ломается до нескольких частей:
(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)
.
Работает хорошо. Один вопрос, я добавил это, чтобы он не добавлял повторяющиеся расширения: if (! Files.Contains (extension)) непосредственно перед файлами строки .Add (расширение); но я все еще вижу дубликаты расширений в конце. Почему он продолжает добавлять дубликаты? –
@ מנימנחם, это потому, что вы рекурсивно вызываете 'SearchAccessibleFiles', новый экземпляр списка' files', созданный каждый раз, когда вы вызываете 'SearchAccessibleFiles' в отношении области действия, и вы будете добавлять этот список usnig' files.AddRange' взамен. поэтому 'if (! files.Contains (extension))' принимает только дубликаты файлов внутри этого каталога, ни верхнюю директорию, ни дочернюю директорию. – hsh
@ מנימנחם, вы поняли, где вы ошибаетесь? – hsh