Один элемент удаляется при итерации IEnumerable <T> - PullRequest
0 голосов
/ 17 октября 2019

У меня есть метод, который берет 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 в список, а затем используя список для дальнейшей работы (содержимое списка остается тем же после цикла).

Что может быть причиной этой проблемы?

1 Ответ

3 голосов
/ 17 октября 2019

Ознакомьтесь с документацией по Enumerable.Where. В частности, Примечания.

Этот метод реализован с использованием отложенного выполнения. Немедленное возвращаемое значение - это объект, в котором хранится вся информация, необходимая для выполнения действия. Запрос, представленный этим методом, не выполняется до тех пор, пока объект не будет перечислен путем непосредственного вызова метода GetEnumerator или использования foreach в Visual C # или For Each в Visual Basic.

Что означает, что при вызовеWhere вы не обязательно возвращаете объект, такой как List или Array, в котором есть только X элементов. Вы получаете объект, который знает, как фильтровать IEnumerable<T>, который вы назвали Where, на основе предоставленного вами предиката. Когда вы выполняете итерацию этого объекта, например, с помощью цикла foreach или вызова Enumerable.Count(), каждый элемент в источнике IEnumerable<T> сравнивается с предоставленным вами предикатом, и возвращаются только элементы, удовлетворяющие этому предикату.

Поскольку предикат, который вы предоставляете, проверяет свойство Sequence, и вы изменяете это свойство в первом цикле foreach, во второй раз, когда вы итерируете entitiesToUpdateSequence, меньшее количество элементов соответствует предоставленному вами предикату, и поэтому выполучить более низкий счет. Если бы вы увеличили Sequence вместо того, чтобы уменьшить его, вы можете получить большее число при повторении итерации entitiesToUpdateSequence.

.
...