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

IEnumerable.Count()第二次调用返回0的问题排查

IEnumerable两次Count()结果不一致的问题解析

问题现象

编写单元测试对比IEnumerable中的预期值与实际值时,第一次调用Count()两者数量相等,但第二次调用时,expectedValues.Count()返回0,actualValues.Count()却保持不变。

代码复现

失败场景(未使用ToList())

using StreamReader sr = new(filepath);
using CsvReader cr = new(sr, CultureInfo.InvariantCulture);
IEnumerable<ExpectedT> expectedValues = cr.GetRecords<ExpectedT>();
IEnumerable<ActualT> actualValues = GetActual();

// 第一次断言通过
Assert.AreEqual(expectedValues.Count(), actualValues.Count(), "1st assert");
// 第二次断言失败:expectedValues.Count()返回0,actualValues.Count()正常
Assert.AreEqual(expectedValues.Count(), actualValues.Count(), "2nd assert");

成功场景(使用ToList()转换)

using StreamReader sr = new(filepath);
using CsvReader cr = new(sr, CultureInfo.InvariantCulture);
List<ExpectedT> expectedValues = cr.GetRecords<ExpectedT>().ToList();
List<ActualT> actualValues = GetActual().ToList();

// 两次断言均通过
Assert.AreEqual(expectedValues.Count, actualValues.Count, "1st assert");
Assert.AreEqual(expectedValues.Count, actualValues.Count, "2nd assert");

原因分析

核心问题在于IEnumerable的延迟执行特性和流的一次性读取限制:

  • 延迟执行(Deferred Execution):IEnumerable<T>本身不存储数据,每次调用Count()、foreach等枚举操作时,才会触发数据的获取逻辑。
  • 流的读取特性:CsvReader.GetRecords<ExpectedT>()依赖StreamReader读取CSV文件,第一次调用Count()时会完整枚举所有记录,此时StreamReader的指针已移动到流的末尾。
  • 第二次枚举失效:第二次调用expectedValues.Count()时,会再次触发枚举逻辑,但流已无剩余数据可读,因此返回0。
  • actualValues无问题的原因:GetActual()返回的IEnumerable是通过内存中直接创建ActualT实例生成的,每次枚举都会重新生成(或读取已存在的内存集合),不受流的限制,因此Count()结果始终一致。

解决方案说明

使用ToList()将IEnumerable转换为List时,会立即执行枚举逻辑,将所有数据加载到内存中存储。后续调用Count时,直接读取List的内置属性,不会再次触发流的读取或数据生成逻辑,因此两次断言都能得到正确结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 08:01:22