【问题标题】:Concat causes stack overflow if called multiple times如果多次调用 Concat 会导致堆栈溢出
【发布时间】:2014-09-27 04:45:30
【问题描述】:

应用程序因堆栈溢出错误而意外崩溃。经过研究,我发现崩溃的原因是以下代码:

foreach (var item in items)
{
    result = result.Concat(item.Data);
}

这是多个IEnumerables 的串联。当items 包含 10,000 个元素时,应用程序崩溃。

SelectMany 修复了这个问题。不过还是……

为什么Concat 扩展在这里会导致堆栈溢出?

【问题讨论】:

  • 再一次:如果我能教给大家关于 LINQ 的一件事,那就是 查询表达式的结果是一个可以执行查询的对象,而不是 执行查询的结果.
  • 另见 en.wikipedia.org/wiki/Joel_Spolsky 的 Schlemiel-the-painter 部分。你已经实现了这个算法的延迟执行版本。

标签: c# .net linq concatenation stack-overflow


【解决方案1】:

记住Concat 的结果不是一个集合——它是一个查询

所以你的“结果”是有效的

Enumerable.Concat(item10000.Data, 
                  Enumerable.Concat(item9999.Data, 
                                  ....
                                    Enumerable.Concat(item2.Data, 
                                                      item1.Data))));

生成嵌套查询时会导致堆栈溢出。

另一种选择是创建一个列表并每次都添加到它:

var list = new List<something>();
foreach (var item in items)
{
    list.AddRange(item.Data);
}

这基本上是 SelectMany 所做的(但使用延迟枚举器,而不是 List):

result = items.SelectMany(item => item.Data);

【讨论】:

    【解决方案2】:

    当您要求第 10,000 个 concat 获得其结果时,它会询问第 9,999 个 concat 操作以获得 结果,目的是在完成后产生更多值,然后第 9,999 个 concat 操作将向第 9,998 个 concat 询问其结果,该操作将向第 9,997 个 concat 询问其结果,最终您要么进入第 1 个 concat,要么耗尽堆栈空间。

    正如您在问题中提到的,SelectMany 是展平序列序列的正确操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-26
      • 2020-12-15
      • 1970-01-01
      • 2016-02-09
      • 2012-08-27
      • 2020-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多