Нет ничего плохого в использовании переменной счетчика. Фактически, используете ли вы for
, foreach
while
или do
, переменная счетчика должна где-то быть объявлена и увеличена.
Так что используйте эту идиому, если вы не уверены, что у вас есть соответственно проиндексированная коллекция:
var i = 0;
foreach (var e in collection) {
// Do stuff with 'e' and 'i'
i++;
}
Иначе используйте этот вариант, если вы знаете , что ваша индексируемая коллекция имеет значение O (1) для доступа к индексу (что будет для Array
и, вероятно, для List<T>
( в документации ничего не сказано), но не обязательно для других типов (например, LinkedList
)):
// Hope the JIT compiler optimises read of the 'Count' property!
for (var i = 0; i < collection.Count; i++) {
var e = collection[i];
// Do stuff with 'e' and 'i'
}
Никогда не нужно «вручную» управлять IEnumerator
, вызывая MoveNext()
и опрашивая Current
- foreach
, что спасает вас от этого конкретного беспокойства ... если вам нужно пропустить элементы, просто используйте continue
в теле цикла.
И просто для полноты, в зависимости от того, что вы делаете с вашим индексом (приведенные выше конструкции предлагают большую гибкость), вы можете использовать Parallel LINQ:
// First, filter 'e' based on 'i',
// then apply an action to remaining 'e'
collection
.AsParallel()
.Where((e,i) => /* filter with e,i */)
.ForAll(e => { /* use e, but don't modify it */ });
// Using 'e' and 'i', produce a new collection,
// where each element incorporates 'i'
collection
.AsParallel()
.Select((e, i) => new MyWrapper(e, i));
Мы используем AsParallel()
выше, потому что уже 2014 год, и мы хотим эффективно использовать эти несколько ядер, чтобы ускорить процесс. Кроме того, для «последовательного» LINQ, вы получаете метод расширения ForEach()
только для List<T>
и Array
... и не ясно, что его использование лучше, чем простое foreach
, поскольку вы все еще используете однопоточный для более уродливого синтаксиса.