Вы действительно должны использовать новые EnumerateFiles перегрузки, чтобы избежать получения всего списка в памяти.
foreach (var f in di.EnumerateFiles("*", SearchOption.AllDirectories)) {
write.WriteLine(f.FullName + "::" + f.CreationTime.ToShortDateString());
}
Вы можете дополнительно оптимизировать свой код, написав каждую строку по мере ее перечисления. Вы можете избежать манипулирования потоком все вместе. Вся ваша рутина становится всего несколькими строками:
public static void GenerateList(String dirPath, String fileName) {
var dir1 = new DirectoryInfo(dirPath);
try {
var lines = from f in dir1.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
select f.FullName + "::" + f.CreationTime.ToShortDateString();
File.WriteAllLines(fileName, lines);
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}
UPDATE
Хорошо, так что отсутствие .Net 4.0 - облом. Однако, вы можете написать свой собственный класс для перечисления файловой системы без особых проблем . Вот тот, который я только что написал, с которым можно играть, и он использует только вызовы API, которые уже доступны для .Net 3.5
public class FileSystemInfoEnumerator: IEnumerator<FileSystemInfo>, IEnumerable<FileSystemInfo> {
private const string DefaultSearchPattern = "*.*";
private String InitialPath { get; set; }
private String SearchPattern { get; set; }
private SearchOption SearchOptions { get; set; }
private Stack<IEnumerator<FileSystemInfo>> EnumeratorStack { get; set; }
private Action<Exception> ErrorHandler { get; set; }
public FileSystemInfoEnumerator(String path, String pattern = DefaultSearchPattern, SearchOption searchOption = SearchOption.TopDirectoryOnly, Action<Exception> errorHandler = null) {
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentException("path cannot be null or empty");
var dirInfo = new DirectoryInfo(path);
if(!dirInfo.Exists)
throw new InvalidOperationException(String.Format("File or Directory \"{0}\" does not exist", dirInfo.FullName));
InitialPath = dirInfo.FullName;
SearchOptions = searchOption;
if(String.IsNullOrWhiteSpace(pattern)) {
pattern = DefaultSearchPattern;
}
ErrorHandler = errorHandler ?? DefaultErrorHandler;
EnumeratorStack = new Stack<IEnumerator<FileSystemInfo>>();
SearchPattern = pattern;
EnumeratorStack.Push(GetDirectoryEnumerator(new DirectoryInfo(InitialPath)));
}
private void DefaultErrorHandler(Exception ex) {
throw ex;
}
private IEnumerator<FileSystemInfo> GetDirectoryEnumerator(DirectoryInfo directoryInfo) {
var infos = new List<FileSystemInfo>();
try {
if (directoryInfo != null) {
var info = directoryInfo.GetFileSystemInfos(SearchPattern);
infos.AddRange(info);
}
} catch (Exception ex) {
ErrorHandler(ex);
}
return infos.GetEnumerator();
}
public void Dispose() {
foreach (var enumerator in EnumeratorStack) {
enumerator.Reset();
enumerator.Dispose();
}
}
public bool MoveNext() {
var current = Current;
if (ShouldRecurse(current)) {
EnumeratorStack.Push(GetDirectoryEnumerator(current as DirectoryInfo));
}
var moveNextSuccess = TopEnumerator.MoveNext();
while(!moveNextSuccess && TopEnumerator != null) {
EnumeratorStack.Pop();
moveNextSuccess = TopEnumerator != null && TopEnumerator.MoveNext();
}
return moveNextSuccess;
}
public void Reset() {
EnumeratorStack.Clear();
EnumeratorStack.Push(GetDirectoryEnumerator(new DirectoryInfo(InitialPath)));
}
public FileSystemInfo Current {
get {
return TopEnumerator.Current;
}
}
object IEnumerator.Current {
get {
return Current;
}
}
public IEnumerator<FileSystemInfo> GetEnumerator() {
return this;
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
IEnumerator<FileSystemInfo> TopEnumerator {
get {
if(EnumeratorStack.Count > 0)
return EnumeratorStack.Peek();
return null;
}
}
private Boolean ShouldRecurse(FileSystemInfo current) {
return current != null &&
IsDirectory(current) &&
SearchOptions == SearchOption.AllDirectories;
}
private Boolean IsDirectory(FileSystemInfo fileSystemInfo) {
return fileSystemInfo != null &&
(fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
}
}
Использовать его довольно просто, просто создайте его с нужными вам опциями, а затем используйте его как любой IEnumerable
.
var fileSystemEnumerator = new FileSystemInfoEnumerator("C:\\Dir",
searchOption: SearchOption.AllDirectories,
errorHandler: Console.WriteLine);
var lines = from f in fileSystemEnumerator
select f.FullName + "::" + f.CreationTime.ToShortDateString();
File.WriteAllLines("FileNames.txt", lines);
Теперь, очевидно, это не так эффективно, как в .Net 4.0, но объем памяти должен быть приемлемым. Я проверил это в каталоге с 50K + файлами, и он закончился примерно через 5 секунд.
Надеюсь, это поможет вам!