2

Regarding the collections implementing this[int] and assuming the collection won't change during the enumeration, does the foreach (var item in list) loop produce the same sequence as for (var i = 0; i < list.Count; ++i) anytime?

This means, when I need the ascending order by index, could I use foreach or is just simply safer to use for? Or it just depends on the curren collection implementation and migh vary or change in time?

sharpener
  • 1,383
  • 11
  • 22
  • 2
    Normally `foreach` is based on the `GetEnumerator` method (except sometimes on arrays). – CodesInChaos Sep 08 '14 at 12:19
  • if you need to know the index, use `for` otherwise, its your choice – Sayse Sep 08 '14 at 12:20
  • Unless there's a performance reason, `foreach` is more concise and less likely to contain silly bugs over `for` followed by an index based lookup. I'd prefer it almost every time. – spender Sep 08 '14 at 12:23
  • 1
    @PatrickHofman `++i` is pretty common as well, especially with C++ programmers. It can yield better performance if `i` is a complex type. – CodesInChaos Sep 08 '14 at 12:24
  • @CodesInChaos - Do you have any reference to that fact? (I'm curious as to how it could be) – Sayse Sep 08 '14 at 12:24
  • I `++i` because logically, it's one less op, although the compiler/jitter probably takes care of it for you. – spender Sep 08 '14 at 12:25
  • Already answered [here](http://stackoverflow.com/questions/10929586/what-is-the-difference-between-for-and-foreach). – Ognyan Dimitrov Sep 08 '14 at 12:25
  • 1
    @Sayse `i++` modifies the variable but returns the original value. So it needs to create a copy. If that copy cannot be optimized out, it decreases performance. For integers any decent compiler will optimize it, but in C++ it's common to use more complex iterators where the compiler might not be able to do that. In C# it should never matter, since you can't overload `i++` or `++i` directly, you only overload a shared increment function. – CodesInChaos Sep 08 '14 at 12:26
  • @CodesInChaos - Interesting, thanks. – Sayse Sep 08 '14 at 12:27
  • 1
    @Sayse it's been asked [here](http://stackoverflow.com/questions/24901/is-there-a-performance-difference-between-i-and-i-in-c) as well. – default Sep 08 '14 at 12:40
  • @Ognyan Dimitrov thanks, I missed this. – sharpener Sep 08 '14 at 12:44

4 Answers4

2
foreach (var item in list)
{
    // do things
}

translates to

var enumerator = list.GetEnumerator();
while(enumerator.MoveNext())
{
   var item = enumerator.Current;
   // do things
}

So as you can see, it's not using the indexor list[i] in the general case. For most collections types, however, the semantics is the same.

edit

There are IList<T> implementations where the enumerator IList<T> as a linked list, it's very unlikely you will use the indexor in your enumerator implementation, as it would be very inefficient. As a rule of thumb, using foreach ensure you use the most efficient algorithm for the class at hand, as it is the one chosen by the class' Creator. In the worst case, you will just suffer a small indirection overhead that is very unlikely to be noticeable.

edit 2 after nos's comment

There is a case where the semantics of the two constructs varies widly: the collection modification.

While using a simple for loop, nothing particular will happen if you change the collection while iterating through it. The program will behave as if it assumed you know what you're doing. This could result in some values iterated over more than once or other skipped, but no exception as long as you're not accessing outside of the range of the indexor (which would require a multithreaded program ot happen).

While using a foreachloop; if you modify the collection while iterating through it, you enter undefined behavior. The documentation tells us

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined. In that case, expect most of C# built-in types to throw an InvalidOperationException, but everything can happen in a custom implementation, from missed values to repeated values , including infinite loops...

Falanwe
  • 4,636
  • 22
  • 37
  • If the `IList` in question is the `List` provided by the framework then the indexer is used internally, but this isn't specific to `IList`, it just happens to be the way it is implemented. – Adam Houldsworth Sep 08 '14 at 12:28
  • @AdamHouldsworth : so very true. But as I pointed out in my answer, it's not generally the case. For instance, you can easily create a linked list that implements `IList`. In that case, the enumerator is very unlikely to use the indexor internally (as the indexor in a linked list is not very efficient). – Falanwe Sep 08 '14 at 12:34
  • OP did not mention any `IList`, just a variable called `list` that is a collection implementing `this[int]`. – Gutblender Sep 08 '14 at 12:44
  • 1
    @Gutblender : He put IList in the tags, so I assume that's what he had in mind. – Falanwe Sep 08 '14 at 12:45
  • 2
    Of particular interest is the case where you modify the collection you're iterating over, which would behave very differently using `foreach` vs `for` – nos Sep 08 '14 at 12:45
  • @nos : very important comment. I'm adding it to my answer right away. – Falanwe Sep 08 '14 at 12:46
1

Generally speaking, yes, but strictly spoken: no. It really depends on the implementation.

Usually with for you would use the this indexer properties. foreach uses GetEnumerator() to get the enumerator that iterates over the collection. Depending on the implementation the enumerator might yield another result than the for.

The implied logic of a list is that is has a specific order, and when implementing IList you may state is it save to assume that the order of both the indexer properties as the enumerator are the same.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • 1
    I'd argue that the indexer and enumeration returning different sequences violates the implied contract of `IList` (even if microsoft might not have bothered to specify that contract explicitly) – CodesInChaos Sep 08 '14 at 12:22
  • 1
    I didn't mention `IList`, nor did OP. For `IList` and arrays I would agree the sequence would be the same. – Patrick Hofman Sep 08 '14 at 12:23
  • The question is tagged with [ilist] – CodesInChaos Sep 08 '14 at 12:24
  • Please be more specific (yes to which question?) Because the answer is really **no**, then **yes**: No, there is no indexer involved, and [`IEnumerator`](http://msdn.microsoft.com/en-us/library/system.collections.ienumerator(v=vs.110).aspx) nowhere uses an index, but yes you can *normally* count on things being iterated in order. – Gutblender Sep 08 '14 at 12:25
1

There is no guarantee that this would be the case. The code paths can be completely separate. Of course collections like List will produce the same result but you can write data structures (even useful ones) that do not.

The indexer is just a property with additional index argument. You can return a random value if you feel like it.

Stilgar
  • 22,354
  • 14
  • 64
  • 101
0

One important think you should have in mind, as a difference between the 2 is that inside foreach you can/t make any changes to the enumarared objects. If you wish to alter (basicaly delete) objects from the enumeration you must use a for loop

apomene
  • 14,282
  • 9
  • 46
  • 72