I'm trying to create an IEnumerator
based on the Directory.EnumerateFiles
as you can see below. My enumerator works on top of Directory.EnumerateFiles
by using LINQ .Select
to convert the FileInfo
into a specialized class MyFile
.
struct Enumerator : IEnumerator<MyFile>
{
private readonly Lazy<IEnumerator<MyFile>> enumerator;
public Enumerator(DirectoryInfo directory)
{
enumerator = new Lazy<IEnumerator<MyFile>>(() =>
{
return directory
.EnumerateFiles($"*{MyFile.Extension}", SearchOption.TopDirectoryOnly)
.OrderBy(f => f.Name)
.Select(MyFile.Read)
.GetEnumerator();
});
}
WalFile IEnumerator<MyFile>.Current => enumerator.Value.Current;
object IEnumerator.Current => enumerator.Value.Current;
void IDisposable.Dispose() => enumerator.Value.Dispose();
bool IEnumerator.MoveNext() => enumerator.Value.MoveNext();
void IEnumerator.Reset() => enumerator.Value.Reset();
}
But it only works when I iterate once. If I iterate the enumerator twice, the 2nd time it's empty. Even removing the Lazy
, it doesn't change the result.
This IEnumerator
is used in another class such as MyFileCollection : IEnumerable<MyFile>
, where I implement IEnumerable
with the specialized Enumerator
class above.
Any insights to be this right?
UPDATE:
Since people started asking why I'm doing this. This question is not about the design. I didn't show every class or the design around it, because I wanted to simplify the question to get an answer of how to use IEnumerator
class.
public interface ILocalRepository : IEnumerable<MyFile>
{
DirectoryInfo Directory { get; }
MyFile? Get(string name);
MyFile Create(MyVersion script);
Task<MyFile> DownloadLatestAsync(IScriptResource resource, string name, CancellationToken cancellation);
Task<MyFile> DownloadAsync(IScriptResource resource, string name, MyVersion version, CancellationToken cancellation);
void Delete(string name);
}
class LocalFileRepository : ILocalRepository
{
protected readonly DirectoryInfo directory;
private readonly Enumerator enumerator;
public LocalFileRepository(DirectoryInfo directory)
{
this.directory = directory;
enumerator = new Enumerator(directory);
}
DirectoryInfo ILocalRepository.Directory => directory;
IEnumerator<MyFile> IEnumerable<MyFile>.GetEnumerator() => enumerator;
IEnumerator IEnumerable.GetEnumerator() => enumerator;
//implementation hidden...
}
CodePudding user response:
Instead of writing a custom enumerator, you could just use the following method:
public IEnumerable<MyFile> EnumerateFiles(DirectoryInfo directory)
{
return directory
.EnumerateFiles($"*{MyFile.Extension}", SearchOption.TopDirectoryOnly)
.OrderBy(f => f.Name)
.Select(MyFile.Read);
}
This is much simpler than creating an enumerator from scratch; if you need the code in various places, you can implement it in a helper class that you inject into the classes that rely on it. This is especially a good way if you need to mock the code in unit tests. If this does not matter, you can also make it a static helper method or an extension method to DirectoryInfo
.