IEnumerable的多次枚举 - StackOverflowException

3
一个关于IEnumerable和Linq的有趣问题。
static void Main(string[] args)
{
    var subject = "#Accountancy #premier #Agile #Apache #automation #Automation #banking #Banking #bankIngs #AutoMation";
    var hashtags = subject.Split('#').Select(hashtag => hashtag.Trim().ToUpper()).Distinct();

    var plurals = hashtags.Where((hashtag) =>
    {
        return hashtags.Contains($"{hashtag.ToUpper()}S");
    }).Select(h => $"{h.ToUpper()}S");      //.ToList(); (1) - will not break

    //filter hashtags
    hashtags = hashtags.Except(plurals);    //.ToList(); (2) - will not break

    //if iterate, would break with:
    //System.StackOverflowException was unhandled Message: An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
    foreach (var hashtag in hashtags)
    {
        Console.WriteLine(hashtag);
    }

    Console.Read();
}

想知道为什么会发生溢出异常吗?


在过滤器中使用一个新变量,以避免在完全迭代之前更改“hashtags”。 - wimh
1
阅读关于Linq和延迟执行的内容,并将其与其他两个答案结合起来。然后你就会明白为什么你的代码会出错。延迟执行可能会导致一些陷阱。 - derpirscher
2个回答

4
请按照以下步骤进行操作:
  1. 复数是指在 hashtags 中每个单词都以 s 结尾的单词。
  2. Hashtags 是指除了复数以外的所有单词。

要执行第二步,您必须先执行第一步。然而,hashtags 不断变化,因此复数试图在不是原始集合的结果上执行(这又依赖于第二步的结果,该结果又依赖于第一步的结果)。

您的查询将尝试执行以下操作:

hashtags = hashtags.Except(plurals);

替换复数形式

hashtags = hashtags.Except(
            hashtags.Where(hashtag => { return hashtags.Contains($"{hashtag.ToUpper()}S"); })
                    .Select(h => $"{h.ToUpper()}S")
           );

但是 hashtagshashtags.Except(plurals);
hashtags.Except(
            hashtags.Except(plurals).Where(hashtag => { return hashtags.Contains($"{hashtag.ToUpper()}S"); })
                    .Select(h => $"{h.ToUpper()}S")
           );

然后我们需要再次替换复数.. 以此类推。

你的修复(添加.ToList())是逻辑上修复它的方式。


3

您正在重新分配未被评估的哈希标签到另一个评估中,从而导致无限循环。如果您将第二个评估放入另一个变量中,则它将起作用:

var hashtags2 = hashtags.Except(plurals);

foreach (var hashtag in hashtags2)
{
    Console.WriteLine(hashtag);
}

1
我认为这不是完整的答案 - hashtags = hashtags.Except(hashtags.Where(h => h.StartsWith("B"))); 不会抛出堆栈溢出异常。 - stuartd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接