for vs foreach for array enumeration

G

garyusenet

I'm a bit confused about the differences of these two commands (what is
the right word for commands here?) when used to enumerate the contents
of an array.

The below example uses both foreach and for to enumerate the contents
of the array.
Also as well as explaining the differences could you explain why the
foreach messagebox isn't working below.

Many TIA.


using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace array_examples
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
int[] intarray = new int[] { 5, 6, 7, 8 };
MessageBox.Show("This example uses for");
for (int i = 0; i < intarray.Length; i++)
{
MessageBox.Show(intarray.ToString());
}
MessageBox.Show("While this example uses foreach");
foreach (int y in intarray)
{
MessageBox.Show(intarray[y].ToString());
}

}
}
}
 
M

Marc Gravell

foreach uses the specific enumerator provided by the class in question (in
this case an array) via the GetEnumerator() method (usually exposed via an
IEnumerable implementation), and on successive loops returns the actual item
(not the index) at that location; so your code should read:
MessageBox.Show(y.ToString());

This adds a little overhead in terms of an enumerator object etc, so
*technically* for very simple items like arrays the indexer performance is
slightly faster. However, in general having to tell the caller what your
implementation (array, collection, list) is a headache, so code tends to be
more reusable using foreach (and IEnumerable).

Additionally, you should generally assume that the provided enumerator is an
appropriate implementation - for instance, in a linked list an enumerator
would simply walk the chain as you "next", but "for" usage using the indexer
(myList etc) could be catastrophic, as it would telescope the effort
(O(n^2) instead of O(n) for enumerator access).

In 2.0 you also have IEnumerable<T> which adds strong typing to the mix.

The other advantage of IEnumerable is that you can use it in cases where
*you don't know how long the list is*. For instance, I could knock together
an IEnumerable wrapper for random data, or a series like Fibonacci, etc - or
(more typically) for lazy reading data where you simply can't tell the size
until it is complete. 2.0 also adds "yield return" to simplify writing
enumerators.

Marc
 
?

=?ISO-8859-15?Q?Tobias_Schr=F6er?=

Marc said:
[..]

Marc

Additionally, while using the foreach loop, you cannot add or remove
items from the array, list, whatever. With fo you can, but be aware that
the indexes change. E.g. you process a list of objects and want to
remove all successfully processed objects. With a foreach loop this is
more difficult. With a for loop you loop back to front (to avoid index
impacts) like

for (int idx; idx = list.Count; --idx;) { // do stuff }

Furthermore the for loop allows you to start and end at any index in the
list or specify any breaking conditions for the loop.

Tobi

Btw.: IMHO, "(control) statement" would be the appropriate word instead
of "command" here.
 
G

garyusenet

Thankyou both very much i've learnt a great deal there.

This is new ground for me. Would someone please mind answering the
following questions that your answers have made me wonder.

1. What is IEnumerable
i've seen this before but don't understand what it is / what it does.

2. What is an enumerator object?

3. What does this mean?
I would like to understand it, but need it explained in as much detail
as possible please.

" for instance, in a linked list an enumerator
would simply walk the chain as you "next", but "for" usage using the
indexer
(myList etc) could be catastrophic, as it would telescope the effort

(O(n^2) instead of O(n) for enumerator access). "

4. What is yield return?

5. What's the difference between a 'control statement' and a statement.


TIA Gary
 
M

Marc Gravell

OK...

IEnumerable is a very simple interface that means "I have contents that you
can query in a sequence"; it does this by a single method: GetEnumerator()
which returns an "enumerator".

An enumerator is an object that implements the IEnumerator interface; this
basically acts as a marker in a sequence, and has the following:
MoveNext() - move forward to the next item in the sequence (returns false if
at the end)
Reset() - go back to the start of the sequence (actually to *before* the
first item; MoveNext() must then be called to get to the first item)
Current - obtains the current item from the list.

Essentially, then, this is all a shortcut:
foreach(int y in array) {
// do something with y
}
really just means:
{
IEnumerator enumerator = array.GetEnumerator();
while(enumerator.MoveNext()) {
int y = (int) enumerator.Current
// do something with y
}
// dispose if disposable
IDisposable disposeMe = enumerator as IDisposable;
if(disposeMe!=null) disposeMe.Dispose();
}

Quite the short cut!
IEnumerable<T> and IEnumerator<T> are the same, but Current is typed as T
instead of object, so IEnumerable<int> works without casting and boxing.

*creating* an enumerator used to be a pain; for an array enumerator, this
would (I'd guess) just be a class with an integer representing the current
index that gets incremented by MoveNext(), and Current just returns the item
at the current index. But it is more complex for harder cases. To help with
this, in 2.0 you can get the compiler to do almost all of the work for you.
For instance, say I want an enumerator that combines two lists, and never
returns "null" values from the first list:

public IEnumerator GetEnumerator()
{
foreach (object item in list1)
{
if(item!=null)
yield return item;
}
foreach (object item in list2)
{
yield return item;
}
}

Job done. This code is invoked when the enumerator is initialised, and each
"yield return" corresponds to an item from MoveNext(). Note that through
some magic this method is interrupted - i.e. it does *NOT* run to completion
before the caller gets the data. The code runs to the first "yield return"
and then this value is returned to the caller via Current. When they
MoveNext() the code continues to the next "yield return" in the loop, etc,
until the method exists, which counts as the end of the data and thus
MoveNext() returns false and the method ends. Very, very sweet. It is
usually used with typed data and IEnumerator<T> etc, but the above works...

Marc
 
?

=?ISO-8859-1?Q?Tobias_Schr=F6er?=

Thankyou both very much i've learnt a great deal there.

This is new ground for me. Would someone please mind answering the
following questions that your answers have made me wonder.

3. What does this mean?
I would like to understand it, but need it explained in as much detail
as possible please.

" for instance, in a linked list an enumerator
would simply walk the chain as you "next", but "for" usage using the
indexer
(myList etc) could be catastrophic, as it would telescope the effort

(O(n^2) instead of O(n) for enumerator access). "


O(n) and O(n^2) refer to the execution time needed by an algorithm.
O(n) means, that execution time is lenear depenend on the number of
items to be prcessed. In a list example, this would be a simple foreach
loop (get each item in the list).
O(n^2) means, that execution time increased quadratically with the
number of items. For example, you loop the list items and for each item
you loop the list again:

foreach (item in list) {
foreach (innerItem in list) {
// do something
}
}

Generally the O-notation gives programmers an idea of how expensive an
algorithm is and allows comparsion of two algorithms that do the same
thing, just in different ways. A classical example might be sorting or
searching lists. Eg. Bubblesort with O(n^2) vs Mergesort with O(n*log(n)).
Search the web or wikipedia for more information on this topic, you
should find plenty :)
5. What's the difference between a 'control statement' and a statement.

Actually, I am not sure about the exact term. I'd use the above as
synonyms (in this context).

1., 2. and 4. where answered by Marc already.

Tobi
 
M

Marc Gravell

Cheers - guess I missed #3, and I agree #5 is unclear.

Gary - to clarify why the indexer usage might be worse for a (hypothetical)
linked list, consider the following rough sketch implementation; the
GetEnumerator() (foreach) method walks the list *once* from end to end. The
indexer approach (for) would (for item i) go through i steps - and hence
would require n(n+1)/2 operations (IIRC), which means O(n^2) [we normally
only care about the higher powers of n]

Marc

private LinkItem GetByIndex(int index)
{
LinkItem item = First;
while (index > 0) item = item.Next;
return item;
}
public T this[int index]
{
get { return GetByIndex(index).Value; }
set { GetByIndex(index).Value = value; }
}
public IEnumerator<T> GetEnumerator()
{
LinkItem item = First;
while(item!=null) {
yield return item.Value;
item = item.Next;
}
}
 
G

garyusenet

Thankyou both very much.

It doesn't all make crystal clear sense, but I guess that will only
come with time.

I know ALOT more about this than when I began a couple of hours ago,
largely thanks to you both. So many, many thanks.

Gary.
 
B

Brian Gideon

I'm a bit confused about the differences of these two commands (what is
the right word for commands here?)

I prefer construct or keyword. That's the terminology I use most often
anyway.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top