Счетчик в цикле foreach в C # - PullRequest
40 голосов
/ 02 декабря 2010

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

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

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

foreach (var item in arr)
{

}  

то, В 1-й итерации item = arr [0];
затем, во 2-м, item = arr [1];
.
.
.
в последнем (nth) item = arr [n-1];

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

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

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

Ответы [ 10 ]

78 голосов
/ 02 декабря 2010

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

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

Варианты ведения индекса:

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

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

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

18 голосов
/ 02 декабря 2010

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

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

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

11 голосов
/ 02 декабря 2010

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

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

То, как Enumerator делает это (используя индекс, оператор yield или магию), является подробностью реализации.

Для достижения того, что вы хотите, вы должны использовать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]
7 голосов
/ 02 декабря 2010

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

int i = 0;
foreach(var x in arr)
{
   //Do some stuff
   i++;
}
2 голосов
/ 02 декабря 2010

Возможно, бессмысленно, но ...

foreach (var item in yourList.Select((Value, Index) => new { Value, Index }))
{
    Console.WriteLine("Value=" + item.Value + ", Index=" + item.Index);
}
1 голос
/ 30 января 2019

Без пользовательского Foreach Версия:

datas.Where((data, index) =>
{
    //Your Logic
    return false;
}).Any();

В некотором простом случае мой способ использует where + false + any.
Это немного меньше, чем foreach + select((data,index)=>new{data,index}), и без специального метода Foreach.

MyLogic:

  • использовать тело оператора запустить вашу логику.
  • , поскольку возвращает false , новый счетчик перечисляемых данных равен нулю.
  • использовать Любой () пусть yeild работает.

Код контрольного теста

[RPlotExporter, RankColumn]
public class BenchmarkTest
{
    public static IEnumerable<dynamic> TestDatas = Enumerable.Range(1, 10).Select((data, index) => $"item_no_{index}");

    [Benchmark]
    public static void ToArrayAndFor()
    {
        var datats = TestDatas.ToArray();
        for (int index = 0; index < datats.Length; index++)
        {
            var result = $"{datats[index]}{index}";
        }
    }

    [Benchmark]
    public static void IEnumrableAndForach()
    {
        var index = 0;
        foreach (var item in TestDatas)
        {
            index++;
            var result = $"{item}{index}";
        }
    }

    [Benchmark]
    public static void LinqSelectForach()
    {
        foreach (var item in TestDatas.Select((data, index) => new { index, data }))
        {
            var result = $"{item.data}{item.index}";
        }
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToList()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToList();
    }

    [Benchmark]
    public static void LinqSelectStatementBodyToArray()
    {
        TestDatas.Select((data, index) =>
        {
            var result = $"{data}{index}";
            return true;
        }).ToArray();
    }

    [Benchmark]
    public static void LinqWhereStatementBodyAny()
    {
        TestDatas.Where((data, index) =>
        {
            var result = $"{data}{index}";
            return false;
        }).Any();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<BenchmarkTest>();

        System.Console.Read();
    }
}

Результат теста:

                         Method |     Mean |     Error |    StdDev | Rank |
------------------------------- |---------:|----------:|----------:|-----:|
                  ToArrayAndFor | 4.027 us | 0.0797 us | 0.1241 us |    4 |
            IEnumrableAndForach | 3.494 us | 0.0321 us | 0.0285 us |    1 |
               LinqSelectForach | 3.842 us | 0.0503 us | 0.0471 us |    3 |
  LinqSelectStatementBodyToList | 3.822 us | 0.0416 us | 0.0389 us |    3 |
 LinqSelectStatementBodyToArray | 3.857 us | 0.0764 us | 0.0785 us |    3 |
      LinqWhereStatementBodyAny | 3.643 us | 0.0693 us | 0.0712 us |    2 |
1 голос
/ 02 декабря 2010

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

0 голосов
/ 02 декабря 2010

Из MSDN:

Оператор foreach повторяет группу встроенных операторов для каждого элемента в массиве или коллекции объектов, которая реализует System.Collections.IEnumerable или System.Collections.Generic.IEnumerable.(Of T) интерфейс.

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

0 голосов
/ 02 декабря 2010

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

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

    class Foo {

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

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

            public bool MoveNext() {
                incantation
            }
        }
    }
0 голосов
/ 02 декабря 2010

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

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

...