我有一个IEnumerable
个对象,我想对它进行一些处理。但是当集合变得太大时,它会在枚举时抛出OutOfMemoryException
,例如在调用Count()
时。
显而易见的解决方案是将集合分成多个部分,这样每个部分都不会太大。但我不知道每个部分中理想数量的物体是多少,我想避免“魔术数字”,因为我不知道每个物体的潜在大小。我甚至不知道将它分成多个部分是一个很好的解决方案。有什么想法吗?
编辑:
在我的代码中我有这个功能。请注意Select
将MyData
投影到一个包装对象集合中,这些对象会增加大小。枚举此集合时,我得到异常(我可以messages.Select(m => new CloudQueueMessage(m.ToBinary())).ToList()
立即获取异常)。
public static void AddMessages(IEnumerable<MyData> messages)
{
DoStuff(messages.Select(m => new CloudQueueMessage(m.ToBinary())));
}
答案 0 :(得分:3)
使用MoreLINQ中的Batch扩展方法,即“将源序列批量化为大小的存储区”。示例如下:
int batchSize = 1000;
var lotsOfItems = Enumerable.Range(0, 10000000);
var batched = lotsOfItems.Batch(batchSize);
foreach (var batch in batched)
{
//handle each batch
}
答案 1 :(得分:3)
Enumerable.Count()
会枚举序列以获取它的计数,如果它是查询而不是集合(那么它使用Count
属性)。但是,即使它枚举了序列,它也不应该抛出OutOfMemoryException
,因为它不会创建新的东西。
我假设您使用的是“非物质化”查询,该查询将在Count()
上执行。如果您使用ToList()
或ToArray()
,也会获得例外。所以你需要向我们展示查询,我们可以尝试在内存消耗方面对其进行优化。
答案 2 :(得分:0)
试试这个
public IEnumerable<IEnumerable<T>> GetPortions<T>(IEnumerable<T> list, int portion)
{
double length = (list.Count() / (double)portion);
for (int i = 0; i < length; i++)
{
yield return list.ToList().Skip(i * portion).Take(portion);
}
}
protected void Page_Load(object sender, EventArgs e)
{
IEnumerable<int> list = Enumerable.Range(1, 25);
foreach (var item in GetPortions(list, 10))
{
}
}