You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Directory.EnumerateFiles实现IEnumerator重复遍历失效问题

问题分析与解决方案

你的代码核心问题是复用了同一个枚举器实例。枚举器(IEnumerator<T>)是带状态的对象,第一次遍历完成后,它的指针已经指向序列末尾,第二次调用MoveNext()会直接返回false,导致遍历结果为空。此外,手动实现的Enumerator结构体也没有解决每次遍历需重新生成序列的问题。

正确实现方式(两种推荐方案)

方案1:直接返回LINQ查询的枚举器(简洁高效)

移除自定义Enumerator结构体,让GetEnumerator()每次返回全新的枚举器实例:

class LocalFileRepository : ILocalRepository
{
    protected readonly DirectoryInfo directory;

    public LocalFileRepository(DirectoryInfo directory)
    {
        this.directory = directory;
    }

    DirectoryInfo ILocalRepository.Directory => directory;

    public IEnumerator<MyFile> GetEnumerator()
    {
        // 每次调用都重新生成文件序列的枚举器
        return directory
            .EnumerateFiles($"*{MyFile.Extension}", SearchOption.TopDirectoryOnly)
            .OrderBy(f => f.Name)
            .Select(MyFile.Read)
            .GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // 其他接口方法实现...
}

方案2:使用yield return语法糖(直观易维护)

利用C#的yield关键字自动生成符合规范的枚举器,无需手动实现IEnumerator<T>:

class LocalFileRepository : ILocalRepository
{
    protected readonly DirectoryInfo directory;

    public LocalFileRepository(DirectoryInfo directory)
    {
        this.directory = directory;
    }

    DirectoryInfo ILocalRepository.Directory => directory;

    public IEnumerator<MyFile> GetEnumerator()
    {
        // 每次遍历都会从头执行这段逻辑,生成全新的序列
        foreach (var file in directory
            .EnumerateFiles($"*{MyFile.Extension}", SearchOption.TopDirectoryOnly)
            .OrderBy(f => f.Name))
        {
            yield return MyFile.Read(file);
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // 其他接口方法实现...
}

可选方案:保留自定义枚举器(不推荐)

如果一定要保留自定义的Enumerator结构体,需确保每次GetEnumerator()都返回全新的实例:

  1. 修改LocalFileRepository,移除复用的枚举器成员:
class LocalFileRepository : ILocalRepository
{
    protected readonly DirectoryInfo directory;

    public LocalFileRepository(DirectoryInfo directory)
    {
        this.directory = directory;
    }

    DirectoryInfo ILocalRepository.Directory => directory;

    public IEnumerator<MyFile> GetEnumerator()
    {
        // 每次都创建新的枚举器实例
        return new Enumerator(directory);
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // 其他接口方法实现...
}
  1. 简化自定义Enumerator结构体(去掉Lazy,因为每次都是新实例):
struct Enumerator : IEnumerator<MyFile>
{
    private readonly IEnumerator<MyFile> enumerator;

    public Enumerator(DirectoryInfo directory)
    {
        enumerator = directory
            .EnumerateFiles($"*{MyFile.Extension}", SearchOption.TopDirectoryOnly)
            .OrderBy(f => f.Name)
            .Select(MyFile.Read)
            .GetEnumerator();
    }

    MyFile IEnumerator<MyFile>.Current => enumerator.Current;
    object IEnumerator.Current => enumerator.Current;
    void IDisposable.Dispose() => enumerator.Dispose();
    bool IEnumerator.MoveNext() => enumerator.MoveNext();
    void IEnumerator.Reset() => enumerator.Reset();
}

核心原理

IEnumerable<T>接口的规范要求:每次调用GetEnumerator()必须返回一个全新的、处于初始状态的枚举器。这样每次遍历都会重新读取目录文件、生成序列,自然支持多次遍历。

内容的提问来源于stack exchange,提问作者JobaDiniz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 21:10:38