假设我有一个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();
}
答案 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
所以看来我认为我需要做的事情,实际上我已经在做了。
我注意尽可能使数据库中的数据尽可能不均匀,以避免获取错误的数据。