这段代码生成一个会导致问题的修改后的闭包警告的反例是什么?

时间:2011-03-24 01:21:35

标签: c# resharper warnings

关于这一点还有其他问题,但这是我的第一次,而且我没有完全理解。

问题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();
    }

这是警告:

enter image description here

1 个答案:

答案 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 变量(当然具有相同的名称),它将是您在评估查询时将看到的值。