循环内的修改元素与for循环混淆

时间:2020-08-01 09:23:18

标签: c# ienumerable

我正在使用以下for循环遍历IEnumerable:

for (int i = 0; i < items.Count(); i++)
{
    if (cancellationToken.IsCancellationRequested)
    {
        return;
    }

    var obj = items.ElementAt(i);

    obj.TranslatedText = await Task.Run(() => Translator.Translate(obj.EnglishText, "English", File.Lang));

    progress.Report(i + 1);

    await Task.Delay(DELAY);
}

上面的代码正在跳过其他元素。即使计数为7,循环也仅运行4次。

我尝试将for循环替换为等效的foreach循环:

int current = 0;
foreach (var item in items)
{
    if (cancellationToken.IsCancellationRequested)
    {
        return;
    }

    item.TranslatedText = await Task.Run(() => Translator.Translate(item.EnglishText, "English", File.Lang));

    progress.Report(++current);

    await Task.Delay(DELAY);
}

工作正常,我无法找出两者之间的区别。

我多挖了一点,发现如果我删除线

obj.TranslatedText = await Task.Run(() => Translator.Translate(obj.EnglishText, "English", File.Lang));

在第一个示例中,它执行得很好。

我不允许修改IEnumerable的内容吗?只是好奇。

更新1 我在下面发布了一个可复制的示例。

https://pastebin.com/5ZXky7iX

1 个答案:

答案 0 :(得分:2)

现在我们有一个完整的示例,我们可以看到问题所在。您用于items的集合是一个取决于TranslatedText的查询:

var source = collection.Where(x => string.IsNullOrEmpty(x.TranslatedText));

对于要处理的项目,对obj采取的操作会使该查询的结果无效:

obj.TranslatedText = "something";

因此,在您的for循环中,最初所有10个Translation对象都满足条件,因此Count()为10。在循环的第一次迭代中,您访问第一个元素(元素0),并将obj.TranslatedText设置为"something"

现在,在循环的每次迭代中,您都在计算“查询的当前结果”,即现在为9。然后,在索引中通过索引访问元素。查询的当前结果-因此,i为1时,您将跳过查询的第一个匹配项,而访问第二个匹配项。但这不是原始集合中的第二个匹配-这是当前查询的第二个匹配 ,它已经跳过了第一个元素,因为您修改了该元素以设置翻译。因此,在循环的第二次迭代中会跳过原始元素索引1,而是为原始元素索引2设置翻译后的文本。然后Count()变成8,依此类推。

使用foreach循环,您只需要遍历一次查询-并且在使“您正在查看的当前元素”的查询条件仍然无效时,查询处理不会无论如何都需要再次检查。

因此,可以使用foreach循环,或者如果要按索引访问元素,则应首先具体化查询。例如,您可以使用:

// Evaluate the query once, storing the results in a list
var list = items.ToList();
// Now you can operate on the list without worrying about the query
// being reevaluated.
for (int i = 0; i < list.Count; i++)
{
    if (cancellationToken.IsCancellationRequested)
    {
        return;
    }
   var obj = list[i];
   obj.TranslatedText = await Task.Run(() => Translator.Translate(obj.EnglishText, "English", File.Lang));
   progress.Report(i + 1);
   await Task.Delay(DELAY);
}
相关问题