为什么可能多次枚举 IEnumerable 警告*未*显示

Why is Possible Multiple Enumeration of IEnumerable warning *not* shown

我有点困惑,为什么 Resharper(也不是 Studio 或 FX Cop:))再次警告我以下代码中可能存在的 IEnumerable 多重枚举:

//warning here fine
 IEnumerable<IFileWrapper> filteredCollection = ctaWrappersContainer.FileContainer.Files.Where(x=>x.IsArchiveEntry);
int y1 = filteredCollection.Count();
int y2 = filteredCollection.Count();

//why no warning here?
int countOfIenumerable = ctaWrappersContainer.FileContainer.Files.Count();
int countOfIenumerableAgain = ctaWrappersContainer.FileContainer.Files.Count();

Files 集合是真正的 IEnumerable,每次调用都会重新计算。 以下是文件 属性 在代码中某处的分配方式:

container.Files = this.GetFilesFromArchive(container, zipFile.FullName, searchPattern);

GetFilesFromArchive() 正在枚举条目并一一返回(基于某些过滤器)。 因此,每次我调用计数时,它都会再次调用(如预期的那样)

 protected override IEnumerable<IFileWrapper> GetFilesFromArchive(FileContainer fileContainer, string zipFilePath, string searchPattern)
        {
           //do some filtering magic on a collection of entries in a zip
          yield return new ZipEntryWrapper(fileContainer, zipEntry, zipFile);
        }

TL/DR:我同意@canton7,这样会导致误报太多。只是不要在属性中放置昂贵的枚举,这是一种不好的做法。

长版:

无法判断枚举是否昂贵
基本上,对可能的多重枚举的检查试图警告您潜在的性能问题,因为 IEnumerable 通常来自数据库查询等昂贵的计算。但是 ReSharper 无法确定枚举是否真的很昂贵,因为追踪所有可枚举的来源将非常复杂且非常缓慢,并且在某些情况下是不可能的(可枚举来自 class 库中的接口或虚方法,并且覆盖可以在外部代码中)。

可枚举属性常用于封装简单的集合
这也适用于可枚举属性:ReSharper 无法确定该可枚举属性是否具有昂贵的枚举。如果它仍然继续并警告相同可枚举 属性 的多个枚举,则会导致太多误报,因为许多程序员不会在属性中放置昂贵的可枚举。大多数情况下,可枚举属性 return 基本集合(如 List 或 HashSet)在幕后和 return 类型 IEnumerable 被选择来封装实现细节,并允许开发人员稍后将实现集合更改为其他东西。尽管现在我们有 IReadOnlyCollection 更适合这种封装,但我们仍然有大量带有 IEnumerable.

的旧代码

属性本来就是轻量级的,不要把昂贵的计算放在那里
我会更进一步并争辩说,即使 ReSharper 可以警告您关于属性的昂贵的多重枚举,对于 return 昂贵的可枚举属性仍然是一个不好的做法。即使您没有在 属性 上枚举两次的单一方法,您仍然可以有一个复杂的方法,它会连续多次调用不同的枚举方法。在这种情况下,你的队友甚至不会考虑缓存对枚举结果的访问,因为属性本​​来就是轻量级的,几乎在所有情况下都缓存它们是没有意义的。