Home > Net >  .NET creating an IEnumerator on top of "GetEnumerator()" method from another class
.NET creating an IEnumerator on top of "GetEnumerator()" method from another class

Time:09-20

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.

  • Related