关于这一点还有其他问题,但这是我的第一次,而且我没有完全理解。
问题1:假设这个例子实际上是安全的,即可以忽略警告:如何更改此代码,以便问题此警告警告将是真实的?
问题2:应用警告的修复程序如何使警告消失?我的直觉告诉我这是同样的结果。
以下是代码:
public static void SynchCreativesForCampaign(int pid, ILogger logger)
{
var db = new SynchDbDataContext(true);
foreach (var creativeItem in CreativeList.Create(pid).CreativeItems)
{
logger.Log(@"creative id " + creativeItem.CreativeId);
var creativeDetail = CreativeDetail.Create(creativeItem.CreativeId);
//var item = creativeItem; <-- this gets added by the "fix" for the warning
var creativeEntity = (from c in db.CreativeEntities
where c.dtid == creativeItem.CreativeId
select c).FirstOrDefault();
if (creativeEntity == null)
{
creativeEntity = new CreativeEntity {dtid = item.CreativeId};
db.CreativeEntities.InsertOnSubmit(creativeEntity);
}
}
db.SubmitChanges();
}
这是警告:
答案 0 :(得分:0)
以下是它可能会咬你的方式。这个例子有点人为,但你会明白这个想法。
public static void SynchCreativesForCampaign(int pid, ILogger logger)
{
var db = new SynchDbDataContext(true);
CreativeEntity creativeEntity = null; // NEW: pull this out of the loop
foreach (var creativeItem in CreativeList.Create(pid).CreativeItems)
{
logger.Log(@"creative id " + creativeItem.CreativeId);
var creativeDetail = CreativeDetail.Create(creativeItem.CreativeId);
if (creativeEntity != null) {
continue; // NEW: let the loop go on, but make sure we only
// wrote to creativeEntity *once*
}
// IMPORTANT: notice I don't immediately call FirstOrDefault!
creativeEntity = from c in db.CreativeEntities
where c.dtid == creativeItem.CreativeId
select c;
}
if (creativeEntity != null)
{
// Only NOW evaluate the query
var result = creativeEntity.FirstOrDefault();
// OK, stop: result holds the creative entity with the dtid
// referring to which CreativeItem.CreativeId?
}
}
您可以回答“使用dtid引用我们循环播放的CreativeItems集合中的第一个项目”,但实际情况是它将引用 last 一个(这就是原因)我让循环continue
- 除了更改creativeItem
的值之外没有做什么,这是让bug浮出水面所必需的。)
代码通过执行一些编译器voodoo来实现,其实质上将creativeItem
变量的生命周期延长到您评估查询的位置。因此,即使在foreach
结束creativeItem
之后看起来不再存在,实际上它在内存中等待,当然它的值与上一次迭代中的值保持不变。
现在考虑如果将creativeItem
的值复制到仅foreach
循环内的范围的另一个变量,会发生什么。现在 变量的生命周期被扩展,但是有一个重要的区别:该变量(在foreach
内部 的范围内)将不会被多次迭代重用。在每次迭代中,将使用 new 变量(当然具有相同的名称),它将是您在评估查询时将看到的值。