위대한 작품은 여기에 확장 FileSystemInfo를 반환하는 코드 문자열 경로 대신에. SearchOption (네이티브 .net과 같은) 추가와 같은 몇 가지 사소한 변경 사항과 초기 디렉토리의 오류 트래핑은 루트 폴더가 액세스가 거부 된 경우를 대비합니다. 원래 게시에 다시 한 번 감사드립니다!
public class SafeFileEnumerator : IEnumerable<FileSystemInfo>
{
/// <summary>
/// Starting directory to search from
/// </summary>
private DirectoryInfo root;
/// <summary>
/// Filter pattern
/// </summary>
private string pattern;
/// <summary>
/// Indicator if search is recursive or not
/// </summary>
private SearchOption searchOption;
/// <summary>
/// Any errors captured
/// </summary>
private IList<Exception> errors;
/// <summary>
/// Create an Enumerator that will scan the file system, skipping directories where access is denied
/// </summary>
/// <param name="root">Starting Directory</param>
/// <param name="pattern">Filter pattern</param>
/// <param name="option">Recursive or not</param>
public SafeFileEnumerator(string root, string pattern, SearchOption option)
: this(new DirectoryInfo(root), pattern, option)
{}
/// <summary>
/// Create an Enumerator that will scan the file system, skipping directories where access is denied
/// </summary>
/// <param name="root">Starting Directory</param>
/// <param name="pattern">Filter pattern</param>
/// <param name="option">Recursive or not</param>
public SafeFileEnumerator(DirectoryInfo root, string pattern, SearchOption option)
: this(root, pattern, option, new List<Exception>())
{}
// Internal constructor for recursive itterator
private SafeFileEnumerator(DirectoryInfo root, string pattern, SearchOption option, IList<Exception> errors)
{
if (root == null || !root.Exists)
{
throw new ArgumentException("Root directory is not set or does not exist.", "root");
}
this.root = root;
this.searchOption = option;
this.pattern = String.IsNullOrEmpty(pattern)
? "*"
: pattern;
this.errors = errors;
}
/// <summary>
/// Errors captured while parsing the file system.
/// </summary>
public Exception[] Errors
{
get
{
return errors.ToArray();
}
}
/// <summary>
/// Helper class to enumerate the file system.
/// </summary>
private class Enumerator : IEnumerator<FileSystemInfo>
{
// Core enumerator that we will be walking though
private IEnumerator<FileSystemInfo> fileEnumerator;
// Directory enumerator to capture access errors
private IEnumerator<DirectoryInfo> directoryEnumerator;
private DirectoryInfo root;
private string pattern;
private SearchOption searchOption;
private IList<Exception> errors;
public Enumerator(DirectoryInfo root, string pattern, SearchOption option, IList<Exception> errors)
{
this.root = root;
this.pattern = pattern;
this.errors = errors;
this.searchOption = option;
Reset();
}
/// <summary>
/// Current item the primary itterator is pointing to
/// </summary>
public FileSystemInfo Current
{
get
{
//if (fileEnumerator == null) throw new ObjectDisposedException("FileEnumerator");
return fileEnumerator.Current as FileSystemInfo;
}
}
object System.Collections.IEnumerator.Current
{
get { return Current; }
}
public void Dispose()
{
Dispose(true, true);
}
private void Dispose(bool file, bool dir)
{
if (file)
{
if (fileEnumerator != null)
fileEnumerator.Dispose();
fileEnumerator = null;
}
if (dir)
{
if (directoryEnumerator != null)
directoryEnumerator.Dispose();
directoryEnumerator = null;
}
}
public bool MoveNext()
{
// Enumerate the files in the current folder
if ((fileEnumerator != null) && (fileEnumerator.MoveNext()))
return true;
// Don't go recursive...
if (searchOption == SearchOption.TopDirectoryOnly) { return false; }
while ((directoryEnumerator != null) && (directoryEnumerator.MoveNext()))
{
Dispose(true, false);
try
{
fileEnumerator = new SafeFileEnumerator(
directoryEnumerator.Current,
pattern,
SearchOption.AllDirectories,
errors
).GetEnumerator();
}
catch (Exception ex)
{
errors.Add(ex);
continue;
}
// Open up the current folder file enumerator
if (fileEnumerator.MoveNext())
return true;
}
Dispose(true, true);
return false;
}
public void Reset()
{
Dispose(true,true);
// Safely get the enumerators, including in the case where the root is not accessable
if (root != null)
{
try
{
fileEnumerator = root.GetFileSystemInfos(pattern, SearchOption.TopDirectoryOnly).AsEnumerable<FileSystemInfo>().GetEnumerator();
}
catch (Exception ex)
{
errors.Add(ex);
fileEnumerator = null;
}
try
{
directoryEnumerator = root.GetDirectories(pattern, SearchOption.TopDirectoryOnly).AsEnumerable<DirectoryInfo>().GetEnumerator();
}
catch (Exception ex)
{
errors.Add(ex);
directoryEnumerator = null;
}
}
}
}
public IEnumerator<FileSystemInfo> GetEnumerator()
{
return new Enumerator(root, pattern, searchOption, errors);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
아마도 병렬 작업을하는 것이 너를 많이 사지는 않을 것이다. 물리 디스크에서 파일을 검색하는 것은 필연적으로 I/O 바운드 작업입니다. –
바보 같은 질문 : 파일 검색이 실제로 필요한 디스크 I/O를 수행합니까? 필자는 디스크의 파일 시스템 구조가 운영 체제 커널에 의해 메모리에 캐싱되고 구조가 디스크의 내용과 분리되어 있기 때문에 필요할 때만 업데이트한다고 생각합니다. 아니? – user979672
검색이 I/O 바인딩 인 경우,'.Parallel()'만 사는 것은'new Foobar()'연산을 스레드하는 것입니다 (시간이 걸릴 수 있으며, 결국 거대한 문자열을 구문 분석해야합니다). 옳은? 'new Foobar()'마다 새로운 쓰레드를 만드는 데 드는 비용이 단일 쓰레드에서 연속적으로'new Foobar()'객체를 생성하는 것보다 비용이 많이 드는 지 궁금합니다. – user979672