i want to discuss this question more theoretically (since it has already enough practical answers)
.net has a very nice abstraction model for groups of data (a.k.a. collections)
IEnumerable
it's just a group of data that you can enumerate. It doesn't matter HOW you enumerate, it's just that you can enumerate some data. And that enumeration is done by a completely different object, an IEnumerator
these interfaces are defined is as follows:
//
// Summary:
// Exposes an enumerator, which supports a simple iteration over a non-generic collection.
public interface IEnumerable
{
//
// Summary:
// Returns an enumerator that iterates through a collection.
//
// Returns:
// An System.Collections.IEnumerator object that can be used to iterate through
// the collection.
IEnumerator GetEnumerator();
}
//
// Summary:
// Supports a simple iteration over a non-generic collection.
public interface IEnumerator
{
//
// Summary:
// Gets the element in the collection at the current position of the enumerator.
//
// Returns:
// The element in the collection at the current position of the enumerator.
object Current { get; }
//
// Summary:
// Advances the enumerator to the next element of the collection.
//
// Returns:
// true if the enumerator was successfully advanced to the next element; false if
// the enumerator has passed the end of the collection.
//
// Exceptions:
// T:System.InvalidOperationException:
// The collection was modified after the enumerator was created.
bool MoveNext();
//
// Summary:
// Sets the enumerator to its initial position, which is before the first element
// in the collection.
//
// Exceptions:
// T:System.InvalidOperationException:
// The collection was modified after the enumerator was created.
void Reset();
}
as you might have noticed, the IEnumerator
interface doesn't "know" what an index is, it just knows what element it's currently pointing to, and how to move to the next one.
now here is the trick: foreach
considers every input collection an IEnumerable
, even if it is a more concrete implementation like an IList<T>
(which inherits from IEnumerable
), it will only see the abstract interface IEnumerable
.
what foreach
is actually doing, is calling GetEnumerator
on the collection, and calling MoveNext
until it returns false.
so here is the problem, you want to define a concrete concept "Indices" on an abstract concept "Enumerables", the built in foreach
construct doesn't give you that option, so your only way is to define it yourself, either by what you are doing originally (creating a counter manually) or just use an implementation of IEnumerator
that recognizes indices AND implement a foreach
construct that recognizes that custom implementation.
personally i would create an extension method like this
public static class Ext
{
public static void FE<T>(this IEnumerable<T> l, Action<int, T> act)
{
int counter = 0;
foreach (var item in l)
{
act(counter, item);
counter++;
}
}
}
and use it like this
var x = new List<string>() { "hello", "world" };
x.FE((ind, ele) =>
{
Console.WriteLine($"{ind}: {ele}");
});
this also avoids any unnecessary allocations seen in other answers.