如何枚举部分列表(以避免OutOfMemoryException)?

时间:2013-04-17 10:20:01

标签: c# .net lazy-loading ienumerable out-of-memory

我有一个IEnumerable个对象,我想对它进行一些处理。但是当集合变得太大时,它会在枚举时抛出OutOfMemoryException,例如在调用Count()时。

显而易见的解决方案是将集合分成多个部分,这样每个部分都不会太大。但我不知道每个部分中理想数量的物体是多少,我想避免“魔术数字”,因为我不知道每个物体的潜在大小。我甚至不知道将它分成多个部分是一个很好的解决方案。有什么想法吗?

编辑:

在我的代码中我有这个功能。请注意SelectMyData投影到一个包装对象集合中,这些对象会增加大小。枚举此集合时,我得到异常(我可以messages.Select(m => new CloudQueueMessage(m.ToBinary())).ToList()立即获取异常)。

    public static void AddMessages(IEnumerable<MyData> messages)
    {
        DoStuff(messages.Select(m => new CloudQueueMessage(m.ToBinary())));
    }

3 个答案:

答案 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))
    {

    }
 }