Счетчик в цикле foreach в C#

работа foreach: Как я знаю,

foreach-это цикл, который перебирает коллекцию или массив по один, начиная с индекса 0 до последний предмет коллекции.

Так, если у меня есть n элементов в массиве.

foreach (var item in arr)
{

}  

затем, In, 1-я итерация, item=arr[0];
затем, во 2-м, item=arr[1];
.
.
.
в прошлом (N-й), item=arr[n-1];

вывод: из работы кажется, что на каждой итерации он знает, какое значение должно быть взято из массива или он знает индекс элемента, который должен быть взят из массива.

Теперь мой вопрос: как я могу получить индекс элемента без использования новой переменной?

foreach (string item in mylist)
{
   if (item == "myitem")
   {
       // get index of item
       break;
   }
}

9 ответов


это зависит от того, что вы подразумеваете под "этим". Итератор знает, какой индекс он достиг, да - в случае a List<T> или массив. Но нет общего индекса внутри IEnumerator<T>. Будет ли это перебором индексированную коллекцию или не до реализации. Многие коллекции не поддерживают прямое индексирование.

(фактически foreach не всегда использует итератор вообще. Если тип компиляции коллекции является массивом, компилятор будет перебирать его, используя array[0], array[1] etc. Аналогично, коллекция может иметь метод под названием GetEnumerator() который возвращает тип с соответствующими членами, но без какой-либо реализации IEnumerable/IEnumerator в поле зрения.)

опции для поддержания индекса:

  • использовать for цикл
  • используйте отдельную переменную
  • используйте проекцию, которая проецирует каждый элемент на пару индекс / значение, например

     foreach (var x in list.Select((value, index) => new { value, index }))
     {
         // Use x.value and x.index in here
     }
    
  • использовать мой SmartEnumerable класс, который немного похож на предыдущий вариант

все, кроме первого из этих параметров, будут работать независимо от того, индексируется ли коллекция естественным образом.


использовать for вместо foreach. foreach не раскрывает свои внутренние работы, он перечисляет все, что есть IEnumerable (который не должен иметь индекс на всех).

for (int i=0; i<arr.Length; i++)
{
    ...
}

кроме того, если вы пытаетесь найти индекс определенного элемента в списке, вам не нужно повторять его вообще самостоятельно. Использовать Array.IndexOf(item) вместо.


понимание foreach является неполным.

он работает с любым типом, который предоставляет IEnumerable (или реализует GetEnumerable методика) и использует возвращенный IEnumerator для перебора элементов в коллекции.

как Enumerator делает это (используя индекс,yield statement или magic) - это деталь реализации.

для того, чтобы достичь того, чего вы хотите, вы должны использовать for петля:

for (int i = 0; i < mylist.Count; i++)
{
}

Примечание:

получить количество элементов в списке немного отличается в зависимости от типа списка

For Collections: Use Count   [property]
For Arrays:      Use Length  [property]
For IEnumerable: Use Count() [Linq method]

или еще проще, если вы не хотите использовать много linq и по какой-то причине не хотите использовать цикл for.

int i = 0;
foreach(var x in arr)
{
   //Do some stuff
   i++;
}

наверное, бессмысленно, но...

foreach (var item in yourList.Select((Value, Index) => new { Value, Index }))
{
    Console.WriteLine("Value=" + item.Value + ", Index=" + item.Index);
}

Это верно только в том случае, если вы выполняете итерацию через массив; что, если вы выполняете итерацию через другой вид коллекции, который не имеет понятия доступа по индексу? В случае массива самый простой способ сохранить индекс-просто использовать цикл vanilla for.


не все коллекции имеют индексы. Например, я могу использовать Dictionary с foreach (и повторите все ключи и значения), но я не могу написать get на отдельных элементах, используя dictionary[0], dictionary[1] etc.

если бы я хотел перебирать словарь и отслеживать индекс, мне пришлось бы использовать отдельную переменную, которую я увеличил сам.


последовательность, итерируемая в цикле foreach, может не поддерживать индексирование или знать такую концепцию, ей просто нужно реализовать метод GetEnumerator, который возвращает объект, который, как минимум, имеет интерфейс IEnumerator, хотя его внедрение не требуется. Если вы знаете, что итерация поддерживает индексирование, и вам нужен индекс, Я предлагаю использовать loop.

пример класса, который можно использовать в foreach:

    class Foo {

        public iterator GetEnumerator() {
            return new iterator();
        }

        public class iterator {
            public Bar Current {
                get{magic}
            }

            public bool MoveNext() {
                incantation
            }
        }
    }

из MSDN:

оператор foreach повторяет группу вложенных операторов для каждого элемент в массиве или объекте коллекция, реализующая Система.Коллекции.IEnumerable или Система.Коллекции.Родовой.IEnumerable(Of Интерфейс Т).

Итак, это не обязательно массив. Это может быть даже ленивая коллекция, не имеющая представления о количестве предметов в коллекции.