迭代 IEnumerable<T> 时删除了一项
One item gets deleted while iterating an IEnumerable<T>
我有一个方法接受 IEnumerable,进一步过滤它并循环过滤后的集合
修改一个 属性.
我观察到一个非常奇怪的行为。
虽然该方法循环遍历过滤后的 IEnumerable<Entity>
,但经过几次迭代(我没有准确计算多少次),
其中一项被删除。
private async Task<bool> UpdateSomeValue(IEnumerable<BusinessEntity> entities, BusinessEntity entityToDelete)
{
//FIlter the IENumerable
var entitiesToUpdateSequence = entities
.Where(f => f.Sequence > entityToDelete.Sequence);
if (entitiesToUpdateSequence.Any())
{
var testList = new List<FormBE>(entitiesToUpdateSequence);
Debug.WriteLine(entitiesToUpdateSequence.Count()); // 5
//DUring this loop, after a few iterations, one item gets deleted
foreach (var entity in testList)
{
entity.Sequence -= 1;
}
Debug.WriteLine(entitiesToUpdateSequence.Count()); // 4
return await _someRepo.UpdateEntitySequence(entityToDelete.Id1, entityToDelete.ID2, testList);
}
return await Task.FromResult(true);
}
这个方法是这样调用的:
var entities = await entitiesTask.ConfigureAwait(false);
var entityToDelete = entities.Single(f => f.Key.Equals("someValue"));
var updated = await UpdateSomeValue(entities, entityToDelete);
仅此而已,没有其他引用 entities
集合。因此,它不能从任何其他线程修改。
我通过将过滤后的 IEnumerable 复制到一个列表中,然后使用该列表进行进一步操作,暂时找到了一个词
(循环后列表内容保持不变)。
可能导致此问题的原因是什么?
查看 Enumerable.Where
上的文档。具体来说,Remarks.
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
这意味着当您调用 Where
时,您不一定会返回一个对象,例如 List
或 Array
,其中只有 X 个项目。您将返回一个对象,该对象知道如何根据您提供的谓词过滤您调用 Where
的 IEnumerable<T>
。当您迭代该对象时,例如使用 foreach
循环或调用 Enumerable.Count()
源 IEnumerable<T>
中的每个项目都将根据您提供的谓词进行评估,并且只有满足该谓词的项目被退回。
由于您提供的谓词检查 Sequence
属性,并且您在第一个 foreach
循环中修改 属性,因此您第二次迭代 entitiesToUpdateSequence
较少的项目与您提供的谓词匹配,因此您得到的计数较低。如果你要增加 Sequence
而不是减少它,你可能会在第二次迭代时得到更高的计数 entitiesToUpdateSequence
.
我有一个方法接受 IEnumerable,进一步过滤它并循环过滤后的集合 修改一个 属性.
我观察到一个非常奇怪的行为。
虽然该方法循环遍历过滤后的 IEnumerable<Entity>
,但经过几次迭代(我没有准确计算多少次),
其中一项被删除。
private async Task<bool> UpdateSomeValue(IEnumerable<BusinessEntity> entities, BusinessEntity entityToDelete)
{
//FIlter the IENumerable
var entitiesToUpdateSequence = entities
.Where(f => f.Sequence > entityToDelete.Sequence);
if (entitiesToUpdateSequence.Any())
{
var testList = new List<FormBE>(entitiesToUpdateSequence);
Debug.WriteLine(entitiesToUpdateSequence.Count()); // 5
//DUring this loop, after a few iterations, one item gets deleted
foreach (var entity in testList)
{
entity.Sequence -= 1;
}
Debug.WriteLine(entitiesToUpdateSequence.Count()); // 4
return await _someRepo.UpdateEntitySequence(entityToDelete.Id1, entityToDelete.ID2, testList);
}
return await Task.FromResult(true);
}
这个方法是这样调用的:
var entities = await entitiesTask.ConfigureAwait(false);
var entityToDelete = entities.Single(f => f.Key.Equals("someValue"));
var updated = await UpdateSomeValue(entities, entityToDelete);
仅此而已,没有其他引用 entities
集合。因此,它不能从任何其他线程修改。
我通过将过滤后的 IEnumerable 复制到一个列表中,然后使用该列表进行进一步操作,暂时找到了一个词 (循环后列表内容保持不变)。
可能导致此问题的原因是什么?
查看 Enumerable.Where
上的文档。具体来说,Remarks.
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
这意味着当您调用 Where
时,您不一定会返回一个对象,例如 List
或 Array
,其中只有 X 个项目。您将返回一个对象,该对象知道如何根据您提供的谓词过滤您调用 Where
的 IEnumerable<T>
。当您迭代该对象时,例如使用 foreach
循环或调用 Enumerable.Count()
源 IEnumerable<T>
中的每个项目都将根据您提供的谓词进行评估,并且只有满足该谓词的项目被退回。
由于您提供的谓词检查 Sequence
属性,并且您在第一个 foreach
循环中修改 属性,因此您第二次迭代 entitiesToUpdateSequence
较少的项目与您提供的谓词匹配,因此您得到的计数较低。如果你要增加 Sequence
而不是减少它,你可能会在第二次迭代时得到更高的计数 entitiesToUpdateSequence
.