Ответ 1
Во-первых, ваш код на самом деле не перебирает по списку дважды, он только выполняет его повторный запуск.
Тем не менее, ваш Select
действительно просто получает последовательность всех индексов; это проще сделать с помощью Enumerable.Range
:
var result = Enumerable.Range(0, lst1.Count)
.Where(i => lst1[i] == "a")
.ToList();
Понимание того, почему список на самом деле не повторяется дважды, немного привыкнет. Я попытаюсь дать основное объяснение.
Вы должны подумать о большинстве методов LINQ, таких как Select и Where as pipe. Каждый метод выполняет небольшую работу. В случае Select
вы даете ему метод, и он по существу говорит: "Всякий раз, когда кто-то спрашивает меня о моем следующем элементе, я сначала спрошу свою входную последовательность для элемента, а затем воспользуюсь методом, который я должен преобразовать в нечто иначе, а затем дайте этот предмет тому, кто меня использует". Where
, более или менее, говорит: "Всякий раз, когда кто-то спрашивает меня об элементе, я попрошу свою входную последовательность для элемента, если функция скажет, что это хорошо, я передам его, если нет, я буду продолжать спрашивать для элементов, пока я не получу тот, который пройдет."
Итак, когда вы связываете их с тем, что происходит, ToList
запрашивает первый элемент, он переходит в Where
, так как он для него первый элемент, Where
переходит в Select
и запрашивает его для первого элемента, Select
идет в список, чтобы задать его первый элемент. Затем список предоставляет первый элемент. Select
затем преобразует этот элемент в то, что ему нужно выплюнуть (в данном случае просто int 0) и присваивает его Where
. Where
принимает этот элемент и запускает его функцию, которая определяет, что она истинна и поэтому выплескивает 0
в ToList
, что добавляет ее в список. Тогда все это происходит еще 9 раз. Это означает, что Select
в конечном итоге будет запрашивать каждый элемент из списка ровно один раз, и он будет передавать каждый его результат непосредственно на Where
, который будет кормить результаты, которые "передают тест" непосредственно в ToList, который хранит их в списке. Все методы LINQ тщательно разработаны, чтобы только когда-либо перебирать исходную последовательность (когда они повторяются один раз).
Обратите внимание, что, хотя это кажется вам сначала сложным, на самом деле для компьютера это довольно легко сделать. Это не так сильно, как может показаться на первый взгляд.