C# 在对象属性上选择Many,然后将结果保存回属性会导致堆栈溢出

C# 在对象属性上选择Many,然后将结果保存回属性会导致堆栈溢出,c#,C#,我有一个简单的类,NumberCollection,它有一个类型为List的属性Numbers 公共类编号集合 { 公共IEnumerable数{get;set;} } 我创建了一个NumberCollection列表: var list=新列表 { 新号码集合 { 数字=新列表{1,2,3}, }, 新号码集合 { 数字=新列表{4,5,6}, }, }; 现在我只想选择list的第一个元素,并将所有NumberCollections的串联Numbers保存到第一个元素。我运行了以下代码:

我有一个简单的类,
NumberCollection
,它有一个类型为
List
的属性
Numbers

公共类编号集合
{
公共IEnumerable数{get;set;}
}
我创建了一个
NumberCollection
列表:

var list=新列表
{
新号码集合
{
数字=新列表{1,2,3},
},
新号码集合
{
数字=新列表{4,5,6},
},
};
现在我只想选择
list
的第一个元素,并将所有
NumberCollections
的串联
Numbers
保存到第一个元素。我运行了以下代码:

IEnumerable<int> a = new[] { 1 };
a = a.SelectMany(_ => a);
a.First();
var singleCollection=list.FirstOrDefault();
singleCollection.Numbers=list.SelectMany(c=>c.Numbers);

它编译和运行正常,但试图访问
singleCollection.Numbers
的任何成员会使调试器崩溃。在即时窗口中计算singleCollection.Numbers.ElementAt(0)会出现stackoverflow异常。这里发生了什么?

您正在创建一个循环引用(因此是stackoverflow)

您正在将列表中的
第一项
分配给
单一集合
,然后尝试将第一项的编号重新分配到其自己的
编号
属性中。您需要通过
ToList
添加新的引用,或者通过
Concat
仅添加值

    var singleCollection = list.FirstOrDefault();
    singleCollection.Numbers = list.SelectMany(c => c.Numbers).ToList() ;
    Console.WriteLine(singleCollection.Numbers.ElementAt(0));

    var singleCollection2 = list.FirstOrDefault();
    singleCollection2.Numbers.Concat(list.SelectMany(c => c.Numbers)); 
    Console.WriteLine(singleCollection2.Numbers.ElementAt(0));
编辑时,您也可以使用“选择”创建新的“引用”列表,然后创建NumberCollection的新实例

  var singleCollection3 = list.Select(x => new NumberCollection { Numbers = x.Numbers.ToList() }).FirstOrDefault();
    singleCollection3.Numbers = list.SelectMany(c => c.Numbers);
    Console.WriteLine(singleCollection3.Numbers.ElementAt(0));

需要注意的重要事项:使用SelectMany时,您使用的是延迟执行。这意味着表达式的计算将延迟到实际使用其值为止。这就是为什么StackOverflow在您实际尝试访问某个号码之前不会发生。

您将从以下代码中获得相同的行为:

IEnumerable<int> a = new[] { 1 };
a = a.SelectMany(_ => a);
a.First();
IEnumerable a=new[]{1};
a=a.SelectMany(=>a);
a、 第一个();

由于
SelectMany()
使用延迟执行,因此在调用
a.First()
之前不会计算其lambda表达式。此时,lambda中引用的
a
不再指向
new[]{1}
。相反,它指向
a.SelectMany(=>a)
,而这只能通过迭代
a
来解决。因此,有一个循环引用,调用堆栈会越来越深,直到dotnet运行时放弃堆栈溢出异常。

您是否可以尝试
list.SelectMany(c=>c.Numbers).ToList()
?@ilkerkaran有效。你能解释一下原因吗?
ToList
实际上是对集合进行快照并从中创建新列表
SelectMany
返回
IEnumerable
。意思是,它一遍又一遍地引用自己。可能需要注意的是,
SelectMany()
,与大多数LINQ运算符一样,使用延迟执行。@StriplingWarrior这会对事情产生什么影响?OP添加了注释,感谢您的建议@StriplingWarrior@stellr42当前位置在评论中很难解释,因此我添加了自己的答案。希望这能说明问题。