我应该总是在函数中返回LINQ查询结果时调用.ToArray吗?

时间:2012-07-19 16:45:33

标签: .net vb.net linq return return-value

在函数中返回LINQ查询的结果时,我遇到了很多Collection was modified; enumeration operation may not execute错误的情况,就像这样......(我应该添加函数作为接口的实现,并且结果使该模块在另一个模块中使用。)

Public Function GetTheFuzzyFuzzbuzzes() As IEnumerable(of FuzzBuzz) _
    Implements IFoo.GetTheFuzzyFuzzBuzzes

    Return mySecretDataSource.Where(Function(x) x.IsFuzzy)
End Function

如果基础数据有可能被更改,我通常在函数或属性getter中返回LINQ查询结果时始终调用.ToArray吗?我知道这样做有一点效率,但我觉得这是安全的事情,所以应该总是这样做,以避免时间耦合问题。

修改

让我更好地解释问题领域。

我们有一个基于图表的实现我们主要关注的领域,这是一个优化问题。实体表示为图节点。用各种成本和其他参数加权的边缘表示节点之间的关系。当用户操纵数据时,我们创建不同的边缘并评估它们可以针对当前状态采取的各种选项,以便为每个选项的结果提供反馈。其他用户和程序对服务器上的数据所做的更改会立即通过推送技术传播到客户端。我们使用了大量的线程......

......所有这些意味着我们以非常异步的方式发生了很多事情。

我们的程序分为模块(基于单一责任原则),包含合同项目和运行时解决的实施项目,这意味着我们非常依赖接口。我们通常使用IEnumerable在模块之间传递数据(因为它们排序不可变)。

4 个答案:

答案 0 :(得分:5)

通常,在返回LINQ查询结果时,您不应始终调用.ToArray.ToList

.ToArray.ToList都是“贪婪”(与懒惰相反)操作,实际上对数据源执行查询。适合他们的地点和时间是架构决策。例如,您可以在项目中建立规则以实现数据访问层内的所有linq查询,从而处理所有数据层异常。或者只要有可能就不执行它们,并且只在最后获得所需的数据。还有许多与此主题相关的其他细节。

但是在从函数返回结果时调用或不调用.ToArray - 这不是一个问题,在你提供更详细的样本之前它没有答案。

答案 1 :(得分:5)

不,我不会对此做出规定。

我理解你的担忧。主叫方可能不知道它的动作会影响查询结果。

在某些情况下,您真的无法做到这一点:

  • 有些例子会导致内存不足,例如无限可枚举,或者每次迭代都会产生新计算图像的枚举器。 (我有两个)。
  • 如果您对查询使用Any()First()。两者都只需要读取第一个元素。所有其他工作都是徒劳的。
  • 如果您希望Enumerables与管道/过滤器链接。实现中间结果只是额外的成本。

另一方面,在很多情况下,当可以想到使用数组会产生影响查询的副作用时,将查询具体化为数组会更安全。

在编写软件时,听起来很有吸引力的规则说“当你需要在X和Y之间选择时,总是做X”。我不相信有这样的规则。也许在15%你真的应该做X,在5%你绝对需要做Y,而对于其他情况,它无所谓。

对于那些剩余的80%,无所事事可能是恰当的。如果您在任何地方插入ToArray(),则代码错误地表明存在这样做的原因。

答案 2 :(得分:2)

如果你要返回一个IEnumerable(或者IQueryable,或类似那些不是自包含的东西),可以调用它的时间,可以用它做什么或者它可以用多长时间的限制需要明确说明。

出于这些原因,我建议如果这是某种API(即图层之间),则返回FuzzBuzz[]而不是IEnumerable<FuzzBuzz>。如果这是类/模块的内部实现的一部分,则更容易证明延迟评估的IEnumerable<FuzzBuzz>,但使用该数组仍然是合理的。

除非结果数量很大,或者频繁调用,否则不太可能是性能问题(在许多情况下,CPU时间很便宜,并且分配给阵列的内存不会保留很长时间)

答案 3 :(得分:2)

“作为一项规则”,不,你不应该总是调用ToList / ToArray。否则,诸如myData.GetSomeSubset().WhereOtherCondition().Join(otherdata)之类的查询会花费大量时间为每个链接的调用分配临时缓冲区。但LINQ最适用于不可变集合。您可能希望在修改mySecretDataSource时更加小心。

具体来说,如果您的代码总是围绕频繁修改数据源进行构建,那么这听起来像是急于返回数组而不是IEnumerable的好理由