Freigeben über


Enumerating over arrays with for vs. foreach

Brad Abrams forwarded an interesting question to me this morning

 

Questions:

What is the difference between enumerating an array using for versus foreach ?

What is the recommended practice and why?

 

I did a quick analysis for him which he has just posted in full.  Have a look see.

Comments

  • Anonymous
    April 29, 2004
    Rico, and what do you say about Joshua Allen's similar analysis on using loops?

    "Loopy Decisions"
    (http://www.netcrucible.com/blog/PermaLink.aspx?guid=1becbd8e-5b9f-40f0-bddf-994aeb580028)

    thanks,
    Slavo.
  • Anonymous
    April 29, 2004
    Well basically he's saying the exact same thing as me. There's no difference between the first two cases and in any case there isn't ever likely to be enough difference that you should change your coding style.

    But the most important thing he says is that, whatever you do, if you're going to make a change like this, do measure it in your own scenarios to see what you'd be getting. Who knows, maybe in a more complex function the optimizer will have a heart-attack and not be able to reduce the code down properly.

    For my part, I just would like to remind you that this is all about arrays and not ArrayLists or other collection classes.

    Quoteing from my other message:

    See the "Enumeration Overhead" section in http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt05.asp -- searching for "foreach" in that same document will give other illustrations and guidance.
  • Anonymous
    May 03, 2004
    Hi Rico,
    A question related to this has been occuring to me also. Whilst the above makes much sense, enumerating an array occurs far less frequently than enumerating a [custom] collection. When I say custom collection, I mean a total, type-safe collection (rather than one being a simple wrapper around ArrayList or something).

    Ie. The data members of such a collection may look similar to:

    private const int DefaultCapacity = 16;
    private int count = 0;
    private Item[] itemList;

    Where itemList expands to reach contain items as they are added. This means that count will never exceed itemList.Length. However, I would guess that the JIT wont be clever enough to reach this conclusion and thus add the bounds checking. Even when an index is made on the collection:

    public Item this[ int index ]
    {
    get
    {
    if ( index < 0 || index >= count )
    throw new IndexOutOfRange...

    return itemList[ index ];
    }
    }

    I already put a bounds check here obviously, but because I'm using 'count' instead of 'itemList.Length' would there be another one added by the JIT? Of course I could look at the debug output, and I will if it becomes a problem for me (at the moment, I'm just interested to see if it may be an issue :)

    Also, if the above does pose a problem for the JIT, is this still applicable when using generic collection in Whidbey. Or is the JIT more intellegent with them (I don't have access to Whidbey atm)?

    I'm more interested in the foreach case in custom collections, as I generally use foreach in this case rather than iterating over them using for.

    Thanks,
    n!
  • Anonymous
    May 03, 2004
    The jit wouldn't have any way of knowing that your "count" is strictly less than the array length in all cases so yes I'd expect you to incur a second test until maybe some day when we could do some very deep analysis and still jit fast enough.

    You could remove the index < 0 test if you're willing to throw the array bounds check exception instead.

    Depending on your use of the collection it may or may not make a difference. I'd expect most times it wouldn't end up mattering much. Usage is everything.
  • Anonymous
    May 03, 2004
    By the way, what you have looks remarkably like System.ArrayList I was wondering why you didn't just use that?
  • Anonymous
    May 04, 2004
    Thanks for the notes! I thought it'd be too much for the JIT but just wondered if I was underestimating it :)

    As for not using ArrayList, it was only an example :) But I usually write the above for value type collections to avoid the boxing penalty of System.ArrayList. As I said, no whidbeygenerics as of yet :(

    Thanks again,
    n!
  • Anonymous
    May 04, 2004
    Gotcha :)
  • Anonymous
    May 26, 2004
    Hi again,
    Sorry to come back to this :) I was thinking, is it possible to provide a hint to the JIT that a local count is in actual fact less than the length of the array. Thus avoiding the bounds check each time.

    For instance, if I have a custom collection and within a collection method I need to scan the array:

    public class MyCollection
    {
    private int count = 0;
    private int[] itemList;

    ... yadda yadda ...

    public bool Contains( int someValue )
    {
    for ( int loop = 0; loop < count; ++loop )
    {
    if ( itemList[ loop ] == someValue )
    return true;
    }

    return false;
    }
    }

    I will get the bounds check, would the JIT understand something like:

    public bool Contains( int someValue )
    {
    if ( count > 0 && count <= itemList.Length )
    {
    for ( int loop = 0; loop < count; ++loop )
    {
    if ( itemList[ loop ] == someValue )
    return true;
    }
    }

    return false;
    }

    Although the first 'if' statement essentially does nothing (I know count is within this range), would the JIT take the hint? Again, please ignore the uselessness of this example :)
    I'm thinking it doesn't (would be nice if it did), but could well be wrong :)

    Thanks,
    n!
  • Anonymous
    June 30, 2004
    I think it's same thing below.

    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();



    for (int i=0; i<assemblies.Length; i++)

    DoSomething(assemblies[i]);



    foreach (Assembly a in assemblies)

    DoSomething(a);