The key to answering this is deferred execution. When you do this
items = items.Select(item => items.First(i => i == item));
you do not iterate the items array passed into the method. Instead, you assign it a new IEnumerable<int>, which references itself back, and starts iterating only when the caller starts enumerating the results.
That is why all your other fixes have dealt with the problem: all you needed to do is to stop feeding IEnumerable<int> back to itself:
- Using
var foobreaks self-reference by using a different variable, - Using
return items.Select...breaks self-reference by not using intermediate variables at all, - Using
ToList()breaks self-reference by avoiding deferred execution: by the timeitemsis re-assigned, olditemshas been iterated over, so you end up with a plain in-memoryList<int>.
But if it’s feeding on itself, how does it get anything at all?
That’s right, it does not get anything! The moment you try iterating items and ask it for the first item, the deferred sequence asks the sequence fed to it for the first item to process, which means that the sequence is asking itself for the first item to process. At this point, it’s turtles all the way down, because in order to return the first item to process the sequence must first get the first item to process from itself.