【发布时间】:2018-11-28 09:56:18
【问题描述】:
有时需要在方法中间实际“评估” IEnumerable,因为它用于多个查询并且编译器发出警告(“IEnumerable 的可能多次枚举”)
var skippedIds = objects.Where(x => x.State=="skip")
.Select(x => x.Id)
.Distinct();
var skippedLookup = skippedIds.ToLookup(x => x.FundId, _ => new { _.Id, _.Name});
if (skippedIds.Any()) // compiler warning
{
...
// other iterations over skippedIds, etc.
}
我曾经这样做过:
var skippedIds = objects.Where(x => x.State=="skip")
.Select(x => x.Id)
.Distinct()
.ToList();
...
但想知道是否有更好的选择。上面的代码在堆上创建了List<T> 对象,我猜这是在方法中死掉的临时变量的上下文中不必要的 GC 负担。
我现在正在使用 ToImmutableArray() 附带的 System.Collections.Immutable 库。 这不仅会创建堆栈分配的对象(不是真的,感谢评论员),而且它还在我的代码中附加了“不可变”语义,我猜这是一种很好的函数式风格练习。
但是性能影响是什么?在方法中本地多个地方使用的“具体化”临时子查询结果的最佳方式是什么?
【问题讨论】:
-
ToImmutableArray比相应的ToList方法调用慢几个数量级,所以为了简单起见,我只使用ToList。 -
如果您真的关心实现结果,那么问题不在于您使用的是
List还是ImmutableArray,而是完全实现结果。您可以在处理结果的foreach中折叠.Any()之类的内容(并检查循环设置的标志)。担心 GC 与不担心 GC 已经是一个比大多数代码需要担心的更高级的话题。 (如果您还没有在任何地方发现瓶颈,甚至担心什么时候实现可能就太过分了。) -
@LasseVågsætherKarlsen 确实如此。例如,编写不佳的 EF 查询可能会不必要地两次访问数据库。但在大多数情况下,这可能没问题。
-
我对列表进行了简单的转换,将列表转换为不可变数组,并对其进行了基准测试。此处的结果和代码:gist.github.com/lassevk/de70f3ab10b120961820de5fd1fd63b5 - 尽管在我的代码和结果中戳个洞,我可能有问题! 请注意,我没有对生成的集合的使用情况进行基准测试,我只对他们的创作进行了基准测试。
-
我可以改用IEnumerable,没问题。你是对的,这发生了巨大的变化 - gist.github.com/lassevk/66936cd630ff2e65d78fcac896c2b4a8 - 所以不要介意我原来的评论,当 ToList 不知道实际的底层大小时,ToImmutableArray 似乎比 ToList 快。
标签: c# performance linq memory functional-programming