10

在函数中返回 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

.ToArray如果基础数据有可能被更改,我是否应该在函数或属性 getter 中返回 LINQ 查询的结果时始终调用?我知道这样做会降低效率,但我觉得这样做是安全的,因此应始终避免出现时间耦合问题。

编辑:

让我更好地解释问题域。

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

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

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

4

4 回答 4

5

一般来说,您不应该总是调用.ToArray.ToList在返回 LINQ 查询的结果时调用。

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

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

于 2012-07-19T17:07:11.730 回答
5

不,我不会为此制定规则。

我理解你的担心。调用方可能不知道它的操作会影响查询结果。

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

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

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

在编写软件时,有这样的规则听起来很有吸引力:“当你需要在 X 和 Y 之间做出选择时,总是做 X”。我不相信有任何这样的规则。也许在 15% 的情况下你真的应该做 X,在 5% 的情况下你肯定需要做 Y,而对于其余的情况,这无关紧要。

对于剩下的 80% 的人来说,什么都不做可能是合适的事情。如果您在ToArray()任何地方插入,代码会错误地暗示这样做是有原因的。

于 2012-07-19T17:53:29.727 回答
2

如果您要返回一个 IEnumerable(或 IQueryable,或类似那些不是自包含的),则限制何时可以调用它、可以用它做什么或可以保持多长时间需要要清楚地写出来。

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

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

于 2012-07-19T17:06:30.253 回答
2

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

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

于 2012-07-19T17:19:09.053 回答