如何逐个生成IEnumerable的元素

时间:2018-03-30 23:07:31

标签: c# .net linq deferred-execution

假设我有一个IEnnumerable<Thing> enumerable对象,并且此列表中的每个Thing生成起来都很昂贵。

当我执行以下代码时,代码到达第1行时会产生相当大的开销,因为据我所知,在开始迭代之前就会生成整个列表。

foreach(Thing t in enumerable)   // line 1
{                                // line 2
    DoStuffWith(t);              // line 3
}

是否有办法根据需要生成每个Thing?喜欢这个

while(enumerable.HasMoreThings())
{
    var e = enumerable.GenerateNextThing();
    DoStuffWith(e);
}

原始代码

using (var db = new PolyPrintEntities())
{
    var sw = new Stopwatch();
    sw.Start();
    IEnumerable<NotificationDto> notifications = db.EmailServiceNotifications
        .Select(not => new NotificationDto
        {
            Notification_ID = not.Notification_ID,
            StoredProcedure = not.StoredProcedure,
            SubjectLine = not.SubjectLine,
            BodyPreface = not.BodyPreface,
            Frequency = not.Frequency,
            TakesDateRange = not.TakesDateRange,
            DateRangeIntervalHours = not.DateRangeIntervalHours,
            TakesAssociateId = not.TakesAssociateId,
            NotificationType = not.NotificationType,
            Associates = not.SubscriptionEvent.SubscriptionSubscribers
                .Select(ss => new AssociateDto
                {
                    Record_Number = ss.Associate.Record_Number,
                    Name = ss.Associate.Name,
                    Email = ss.Associate.EMail
                })

        });
    sw.Stop();
    Console.WriteLine($"Enumeration:{sw.ElapsedMilliseconds}"); //about .5 seconds
    sw.Reset();

    sw.Start();
    //List is generated here, this is where lag happens. 
    int i = 0;
    foreach (var n in notifications)
    {
        if(i == 0)
        {
            sw.Stop();
            Console.WriteLine($"Generation:{sw.ElapsedMilliseconds}"); //about 10 seconds
            i++;
        }
        var ret = n.GetEmails();
        db.EmailQueues.AddRange(ret);
    }
    db.SaveChanges();
}

3 个答案:

答案 0 :(得分:3)

只需yield他们。

while(enumerable.HasMoreThings()) 
{
    yield return enumerable.GenerateNextThing();
}

每当有人想要下一个项目(也许是这个函数的调用者)时,这个函数会懒惰地创建和产生项目。

答案 1 :(得分:2)

Foreach是一种c#语言结构,它变成了与你的第二个代码部分非常相似的东西。 IEnumerable旨在完成所需的操作,只有在您需要时才会一次提供1个项目

答案 2 :(得分:1)

我明白了。

看来,在进一步完善我的代码之后,我获得了一些不符合我的想法的经验数据。

我用sw以不同的方式测量时间:

        using (var db = new PolyPrintEntities())
        {
            var sw = new Stopwatch();
            sw.Start();
            IEnumerable<NotificationDto> notifications = db.EmailServiceNotifications
                .Select(not => new NotificationDto
                {
                    Notification_ID = not.Notification_ID,
                    StoredProcedure = not.StoredProcedure,
                    SubjectLine = not.SubjectLine,
                    BodyPreface = not.BodyPreface,
                    Frequency = not.Frequency,
                    TakesDateRange = not.TakesDateRange,
                    DateRangeIntervalHours = not.DateRangeIntervalHours,
                    TakesAssociateId = not.TakesAssociateId,
                    NotificationType = not.NotificationType,
                    Associates = not.SubscriptionEvent.SubscriptionSubscribers
                        .Select(ss => new AssociateDto
                        {
                            Record_Number = ss.Associate.Record_Number,
                            Name = ss.Associate.Name,
                            Email = ss.Associate.EMail
                        })

                });



            sw.Stop();
            Console.WriteLine($"Enumeration:{sw.ElapsedMilliseconds}"); //about .5 seconds
            sw.Reset();
            sw.Start();

            //Each item appears to be generated One by One 
            int i = 0;
            foreach (var n in notifications)
            {
                sw.Stop();
                Console.WriteLine($"Generation_{i}:{sw.ElapsedMilliseconds}"); //about 1 second
                var ret = n.GetEmails();
                db.EmailQueues.AddRange(ret);
                sw.Start();
                i++;
            }
            db.SaveChanges();
        }

和输出:

Enumeration:404
Generation_0:985
Generation_1:986
Generation_2:986

所以看来我认为我需要做的事情,实际上我已经在做了。

我注意尽可能使数据库中的数据尽可能不均匀,以避免获取错误的数据。