C# 修改循环内的元素会弄乱for循环

C# 修改循环内的元素会弄乱for循环,c#,ienumerable,C#,Ienumerable,我使用以下for循环来迭代IEnumerable: for(int i=0;iTranslator.Translate(obj.EnglishText,“English”,File.Lang)); 进度报告(i+1); 等待任务。延迟(延迟); } 上面的代码正在跳过备用元素。即使计数为7,循环也只运行了4次 我试图用等效的foreach循环替换for循环: int电流=0; foreach(项目中的var项目) { if(cancellationToken.IsCancellationReq

我使用以下
for
循环来迭代IEnumerable:

for(int i=0;iTranslator.Translate(obj.EnglishText,“English”,File.Lang));
进度报告(i+1);
等待任务。延迟(延迟);
}
上面的代码正在跳过备用元素。即使计数为7,循环也只运行了4次

我试图用等效的
foreach
循环替换
for
循环:

int电流=0;
foreach(项目中的var项目)
{
if(cancellationToken.IsCancellationRequested)
{
返回;
}
item.TranslatedText=wait Task.Run(()=>Translator.Translate(item.EnglishText,“English”,File.Lang));
进度报告(++当前);
等待任务。延迟(延迟);
}
它工作得很好,我不知道这两者有什么不同

我又挖了一点,发现如果我把绳子移开

obj.TranslatedText=wait Task.Run(()=>Translator.Translate(obj.EnglishText,“English”,File.Lang))

从第一个例子来看,它执行得很好

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

更新1 我在下面贴了一个可复制的例子


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

var source=collection.Where(x=>string.IsNullOrEmpty(x.TranslatedText));
对于您正在处理的项目,您对
obj
执行的操作将使该查询的结果无效:

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

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

使用
foreach
循环,您只需在查询上迭代一次,而当您仍然使“正在查看的当前元素”的查询条件无效时,查询处理无论如何都不需要再次检查

因此,要么使用
foreach
循环,要么如果希望通过索引访问元素,那么应该首先具体化查询。例如,您可以使用:

//对查询求值一次,将结果存储在列表中
var list=items.ToList();
//现在,您可以对列表进行操作,而不用担心查询
//正在重新评估。
for(int i=0;iTranslator.Translate(obj.EnglishText,“English”,File.Lang));
进度报告(i+1);
等待任务。延迟(延迟);
}

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

var source=collection.Where(x=>string.IsNullOrEmpty(x.TranslatedText));
对于您正在处理的项目,您对
obj
执行的操作将使该查询的结果无效:

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

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

使用
foreach
循环,您只需在查询上迭代一次,而当您仍然使“正在查看的当前元素”的查询条件无效时,查询处理无论如何都不需要再次检查

因此,要么使用
foreach
循环,要么如果希望通过索引访问元素,那么应该首先具体化查询。例如,您可以使用:

//对查询求值一次,将结果存储在列表中
var list=items.ToList();
//现在,您可以对列表进行操作,而不用担心查询
//正在重新评估。
for(int i=0;i