gpt4 book ai didi

c# - 为什么这个方法会导致无限循环?

转载 作者:IT王子 更新时间:2023-10-29 03:43:49 26 4
gpt4 key购买 nike

我的一位同事向我提出了有关导致无限循环的方法的问题。实际代码有点复杂,不能在这里发布,但本质上问题归结为:

private IEnumerable<int> GoNuts(IEnumerable<int> items)
{
items = items.Select(item => items.First(i => i == item));
return items;
}

应该(您会认为)只是创建列表副本的一种非常低效的方法。我称它为:

var foo = GoNuts(new[]{1,2,3,4,5,6});

结果是无限循环。奇怪。

我认为修改参数在风格上是一件坏事,所以我稍微更改了代码:

var foo = items.Select(item => items.First(i => i == item));
return foo;

成功了。即程序完成;也不异常(exception)。

更多实验表明这也有效:

items = items.Select(item => items.First(i => i == item)).ToList();
return items;

简单的

return items.Select(item => .....);

很好奇。

很明显,问题与重新分配参数有关,但前提是评估被推迟到该语句之外。如果我添加 ToList() 它会起作用。

我对出了什么问题有一个笼统的、模糊的想法。看起来 Select 正在迭代它自己的输出。这本身有点奇怪,因为如果 IEnumerable 正在迭代的集合发生更改,通常会抛出异常。

我不明白的是为什么重新分配参数会导致这个无限循环,因为我并不十分熟悉这些东西的工作原理。

有没有懂内幕的人愿意解释一下为什么会出现死循环?

最佳答案

回答这个问题的关键是延迟执行。当你这样做的时候

items = items.Select(item => items.First(i => i == item));

没有迭代items传入方法的数组。相反,您为它分配一个新的 IEnumerable<int> ,它引用自身,并且仅在调用者开始枚举结果时才开始迭代。

这就是为什么所有其他修复程序都解决了这个问题:您需要做的就是停止喂食 IEnumerable<int>回到自身:

  • 使用 var foo通过使用不同的变量打破自引用,
  • 使用 return items.Select...通过完全不使用中间变量来打破自引用,
  • 使用 ToList()通过避免延迟执行来打破自引用:到时间 items重新分配,旧items已经迭代,所以你最终得到一个普通的内存 List<int> .

But if it's feeding on itself, how does it get anything at all?

没错,它没有得到任何东西!当您尝试迭代 items 时并要求它处理第一个项目,延迟序列要求提供给它的序列处理第一个项目,这意味着该序列正在要求自己处理第一个项目。此时,它是turtles all the way down ,因为为了返回要处理的第一个项目,序列必须首先从其自身获取要处理的第一个项目。

关于c# - 为什么这个方法会导致无限循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31993443/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com