Один из моих коллег пришел ко мне с вопросом об этом методе, который приводит к бесконечному циклу. Реальный код немного слишком сложен, чтобы разместить здесь, но по существу проблема сводится к следующему:Почему этот метод приводит к бесконечному циклу?
private IEnumerable<int> GoNuts(IEnumerable<int> items)
{
items = items.Select(item => items.First(i => i == item));
return items;
}
Этого должны (вы могли бы подумать) просто очень неэффективный способ создать копию списка , Я назвал это:
var foo = GoNuts(new[]{1,2,3,4,5,6});
Результат - бесконечный цикл. Странный.
Я думаю, что изменение параметра есть, стилистически плохо, поэтому я немного изменил код:
var foo = items.Select(item => items.First(i => i == item));
return foo;
Это работало. То есть, программа завершена; не исключение.
Другие эксперименты показали, что это работает, тоже:
items = items.Select(item => items.First(i => i == item)).ToList();
return items;
Как делает простой
return items.Select(item => .....);
Любопытный.
Понятно, что проблема связана с переназначением параметра, но только если оценка отложена за пределами этого утверждения. Если я добавлю ToList()
, он будет работать.
У меня есть общее, расплывчатое представление о том, что происходит не так. Похоже, что Select
выполняет итерацию над собственным выходом. Это немного странно само по себе, потому что, как правило, IEnumerable
будет бросать, если коллекция, которую он выполняет, изменяет.
То, что я не понимаю, потому что я не очень хорошо знаком с внутренними функциями того, как работает этот материал, является причиной того, что повторное назначение параметра вызывает этот бесконечный цикл.
Есть ли кто-нибудь с большим знанием внутренних дел, которые хотели бы объяснить, почему здесь происходит бесконечный цикл?
'items' переназначается до начала итерации. Вот почему вы получаете рекурсивный итератор. Этого не происходит, если вы вызываете 'ToList', поскольку назначение выполняется после итерации в этом случае. –
Не отвечая, вы должны знать, что существует много способов клонирования списка. Вот несколько отличных ответов о том, как это сделать. http://stackoverflow.com/questions/222598/how-do-i-clone-a-generic-list-in-c – MiniRagnarok
Спасибо, @MiniRagnarok. Я не пытался клонировать список, а использовал этот простой код, чтобы проиллюстрировать поведение бесконечной рекурсии более сложного метода. –