【发布时间】:2019-11-14 01:38:12
【问题描述】:
我遇到过一段代码,只有当 for 循环重复一定次数时才会导致堆栈溢出异常。
代码如下:
public class Stackoverflow
{
public static void Test()
{
List<int> list = new List<int>() {1, 2};
Container container = new Container {List = list};
for (int i = 0; i < 10000; i++) // This matters
{
foreach (var item in container.List)
{
Console.WriteLine(item);
}
}
}
}
public class Container
{
private IEnumerable<int> list;
public IEnumerable<int> List
{
get
{
//return list.OrderBy(x => x); <- This is OK
list = list.OrderBy(x => x); // This is not
return list;
}
set { list = value; }
}
}
当 Test() 方法执行时,you can see a long series of "1" and "2" being printed on screen before the error actually happens.(我认为这意味着 for 循环正在正常推进)
如果条件变为“i
The memory dump showed "List.Orderby" is likely the direct cause of the problem
我知道写这样的“get”是不好的做法,但我不明白是什么导致了这里的堆栈溢出异常,以及为什么它似乎是累积的而不是递归的死亡陷阱。
这可能是枚举代码中的一个陷阱,或者是编译器的什么,或者只是我的疏忽? 无论如何,正在寻找解释,谢谢您的帮助。
在文本中:
000000e86215e460 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e4a0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e4c0 00007ffe86bad279 (MethodDesc 00007ffe866d7630 +0x19 System.StubHelpers.StubHelpers.SafeHandleRelease(System.Runtime.InteropServices.SafeHandle))
000000e86215e510 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e520 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e560 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e580 00007ffe86c5b552 (MethodDesc 00007ffe867e3f60 +0xf2 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr))
000000e86215e5d0 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e5e0 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e5e8 00007ffe86c5b526 (MethodDesc 00007ffe867e3f60 +0xc6 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr))
000000e86215e620 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e690 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e6a0 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e6e0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e710 00007ffe86b9b46a (MethodDesc 00007ffe86950f90 +0x8a System.IO.StreamWriter.Flush(Boolean, Boolean))
000000e86215e750 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e760 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e7a0 00007ffe849ce396 (MethodDesc 00007ffe844a4ce8 +0x66 System.Linq.Buffer`1[[System.Int32, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<Int32>))
000000e86215e7d0 00007ffe28670da5 (MethodDesc 00007ffe28565c68 +0xe5 ConsoleTest.Container.get_List())
000000e86215e810 00007ffe28670fe7 (MethodDesc 00007ffe28567e80 +0x47 System.Linq.OrderedEnumerable`1+<GetEnumerator>d__1[[System.Int32, mscorlib]].MoveNext())
000000e86215e820 00007ffe28670f7c (MethodDesc 00007ffe28567738 +0x2c System.Linq.OrderedEnumerable`1[[System.Int32, mscorlib]].GetEnumerator())
000000e86215e860 00007ffe2867065f (MethodDesc 00007ffe28565b98 +0x11f ConsoleTest.Stackoverflow.Test())
000000e86215e900 00007ffe286704ba (MethodDesc 00007ffe28565ac0 +0x3a ConsoleTest.Program.Main(System.String[]))
【问题讨论】:
-
我们需要堆栈跟踪。
-
list = list.OrderBy(x => x)构建了一个更大的表达式树。经过几次迭代后,您将拥有<originalList>.OrderBy(x => x).OrderBy(x => x).OrderBy(x => x)...这与return list.OrderBy(x => x)完全不同。 -
我不完全理解你的问题,但这似乎是基于对 LINQ 实际工作方式的误解。 LINQ 查询使用延迟执行;这是众所周知的,很容易搜索。
.OrderBy()充当转换/构建器;在枚举整个查询之前实际上什么都不会发生(这对于 LINQ 允许您为外部提供程序(如数据库)构建查询的方式至关重要)。没有eval()的意思是没有文本被解析为代码,这里 - 而是 LINQ 方法充当构建器。 -
阅读下面彼得斯的回答,它应该告诉你发生了什么。
IEnumerable<T>是延迟评估的东西,如果您将 LINQ 表达式存储到这样的变量中,您将等待执行该表达式,直到您对其进行迭代,因为您将延迟评估的表达式堆叠在延迟评估的表达式之上,最终堆栈倒塌。 -
简短:这:
IEnumerable<int> x = someList.OrderBy(i => i);实际上并没有对列表进行排序。无论列表有多大,这段代码实际上都非常便宜。 但是,您存储的是如何获取值的“配方”,稍后在您迭代此x变量时使用,当您这样做时,首先我们将对列表进行排序,然后我们将返回有序值。如果您甚至订购x,您现在有 2 个订购步骤。
标签: c# stack-overflow