Как вы получаете индекс текущей итерации цикла foreach? - PullRequest
789 голосов
/ 04 сентября 2008

Есть ли какая-нибудь редкая языковая конструкция, с которой я не сталкивался (например, немногие, которые я недавно узнал, некоторые о переполнении стека) в C # для получения значения, представляющего текущую итерацию цикла foreach?

Например, в настоящее время я делаю что-то подобное в зависимости от обстоятельств:

int i = 0;
foreach (Object o in collection)
{
    // ...
    i++;
}

Ответы [ 34 ]

17 голосов
/ 31 декабря 2009

Вот решение, которое я только что нашел для этой проблемы

Оригинальный код:

int index=0;
foreach (var item in enumerable)
{
    blah(item, index); // some code that depends on the index
    index++;
}

Обновленный код

enumerable.ForEach((item, index) => blah(item, index));

Метод расширения:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
    {
        var unit = new Unit(); // unit is a new type from the reactive framework (http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx) to represent a void, since in C# you can't return a void
        enumerable.Select((item, i) => 
            {
                action(item, i);
                return unit;
            }).ToList();

        return pSource;
    }
12 голосов
/ 31 декабря 2009
int index;
foreach (Object o in collection)
{
    index = collection.indexOf(o);
}

Это будет работать для коллекций, поддерживающих IList.

10 голосов
/ 21 июля 2017

C # 7, наконец, дает нам элегантный способ сделать это:

static class Extensions
{
    public static IEnumerable<(int, T)> Enumerate<T>(
        this IEnumerable<T> input,
        int start = 0
    )
    {
        int i = start;
        foreach (var t in input)
        {
            yield return (i++, t);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var s = new string[]
        {
            "Alpha",
            "Bravo",
            "Charlie",
            "Delta"
        };

        foreach (var (i, t) in s.Enumerate())
        {
            Console.WriteLine($"{i}: {t}");
        }
    }
}
10 голосов
/ 04 сентября 2008

Это будет работать только для Списка, а не для любого IEnumerable, но в LINQ есть это:

IList<Object> collection = new List<Object> { 
    new Object(), 
    new Object(), 
    new Object(), 
    };

foreach (Object o in collection)
{
    Console.WriteLine(collection.IndexOf(o));
}

Console.ReadLine();

@ Джонатан Я не сказал, что это был отличный ответ, я просто сказал, что он просто показывает, что можно сделать то, что он просил:)

@ Graphain Я бы не ожидал, что это будет быстро - я не совсем уверен, как это работает, он может повторить весь список каждый раз, чтобы найти подходящий объект, который был бы чертовски похож на сравнения.

Тем не менее, List может хранить индекс каждого объекта вместе с подсчетом.

Кажется, у Джонатана есть идея получше, если он уточнит?

Было бы лучше просто посчитать, где вы находитесь, в foreach, хотя, проще и более адаптируемо.

9 голосов
/ 21 мая 2018

Просто добавьте свой собственный индекс. Сохраняй это простым.

int i = 0;
foreach (var item in Collection)
{
    item.index = i;
    ++i;
}
8 голосов
/ 27 мая 2016

Почему foreach?!

Самый простой способ - использовать для вместо foreach , если вы используете List .

for(int i = 0 ; i < myList.Count ; i++)
{
    // Do Something...
}

ИЛИ если вы хотите использовать foreach:

foreach (string m in myList)
{
     // Do something...       
}

Вы можете использовать это для индексации каждого цикла:

myList.indexOf(m)
8 голосов
/ 21 августа 2010

Это то, как я это делаю, что приятно из-за своей простоты / краткости, но если вы много делаете в теле цикла obj.Value, оно довольно быстро устареет.

foreach(var obj in collection.Select((item, index) => new { Index = index, Value = item }) {
    string foo = string.Format("Something[{0}] = {1}", obj.Index, obj.Value);
    ...
}
5 голосов
/ 21 августа 2010

Лучше использовать ключевое слово continue Безопасная конструкция, как это

int i=-1;
foreach (Object o in collection)
{
    ++i;
    //...
    continue; //<--- safe to call, index will be increased
    //...
}
5 голосов
/ 11 ноября 2016

Ведущий ответ гласит:

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

Хотя это верно для текущей версии C #, это не концептуальное ограничение.

Решением этой проблемы может стать создание новой функции языка C # вместе с поддержкой нового интерфейса IIndexedEnumerable

foreach (var item in collection with var index)
{
    Console.WriteLine("Iteration {0} has value {1}", index, item);
}

//or, building on @user1414213562's answer
foreach (var (item, index) in collection)
{
    Console.WriteLine("Iteration {0} has value {1}", index, item);
}

Если foreach передается IEnumerable и не может разрешить IIndexedEnumerable, но его запрашивают с помощью var index, то компилятор C # может обернуть исходный код объектом IndexedEnumerable, который добавляет код для отслеживания индекса.

interface IIndexedEnumerable<T> : IEnumerable<T>
{
    //Not index, because sometimes source IEnumerables are transient
    public long IterationNumber { get; }
}

Почему:

  • Foreach выглядит лучше, а в бизнес-приложениях редко бывает узким местом производительности
  • Foreach может быть более эффективным в памяти. Наличие конвейера функций вместо преобразования в новые коллекции на каждом этапе. Кому интересно, если он использует несколько больше циклов ЦП, если меньше сбоев ЦП и меньше GC.Collects
  • Требование, чтобы кодер добавил код отслеживания индекса, портит красоту
  • Это довольно легко реализовать (спасибо MS) и обратно совместимо

Хотя большинство людей здесь не являются MS, это правильный ответ, и вы можете лоббировать MS, чтобы добавить такую ​​функцию. Вы уже можете создать свой собственный итератор с функцией расширения и использовать кортежи , но MS может посыпать синтаксический сахар, чтобы избежать функции расширения

5 голосов
/ 05 января 2015

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

foreach (Object o in collection)
{
    // ...
    @collection.IndexOf(o)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...