更改对象正在影响foreach循环中对象的先前版本

时间:2012-10-29 13:56:04

标签: c# reference foreach

所以,我觉得这个很直接。

这是我的代码:

Dictionary<int, IEnumerable<SelectListItem>> fileTypeListDict = new Dictionary<int, IEnumerable<SelectListItem>>();

foreach (PresentationFile pf in speakerAssignment.FKPresentation.PresentationFiles)
{
    IEnumerable<SelectListItem> fileTypes = Enum.GetValues(typeof(PresentationFileType))
                .Cast<PresentationFileType>().Select(x => new SelectListItem
        {
             Text = x.ToString(),
             Value = Convert.ToString((int)x),
             Selected = pf.Type == (int)x
        });

        fileTypeListDict.Add(pf.ID, fileTypes);
}

最后发生的是,字典将包含所有正确的键,但所有值都将设置为循环最后一次迭代期间创建的fileTypes列表。我确信这与用作参考的对象有关,但在我使用C#之前没有看到过这个问题。任何人都在意解释为什么会这样,以及我应该如何解决这个问题?

谢谢!

1 个答案:

答案 0 :(得分:9)

这是臭名昭着的“foreach捕获问题”,并在C#5中“修复”(“修复”是一个强有力的词,因为它表明它之前是一个“错误”:实际上 - 规范现在改为承认这是混乱的常见原因)。在这两种情况下,lambda都会捕获变量 pf而不是“此迭代期间pf的值” - 但在C#之前 - 5变量pf 技术范围循环外(因此只有其中一个,句点),其中 - 如在C#5及以上变量是在循环中设置(因此,为了捕获目的,每次迭代都有一个不同的变量)。

在C#4中,只是作弊:

foreach (PresentationFile tmp in speakerAssignment.FKPresentation.PresentationFiles)
{
    PresentationFile pf = tmp;
    //... as before

现在pf的范围是foreach,它可以正常工作。没有它,只有一个pf - 并且因为你将执行推迟到结束,单个pf将是最后一次迭代。

另一种解决方法是:不要推迟执行:

fileTypeListDict.Add(pf.ID, fileTypes.ToList()); // note the ToList

现在评估pf是“当前”,因此会得到您期望的结果。