Why does foreach raise exception when no elements of specified typeexist?

  • Thread starter Thread starter Brad Wood
  • Start date Start date
B

Brad Wood

When I do this:
foreach( Button btn in myForm.Controls )

An exception is raised when no buttons exist. I would simply expect
execution to continue after the foreach loop (just as would be the case
with a regular for loop when the second expression is false when the
loop begins).

Is there an explanation for this?
 
Hi,


No, an exception is throw when the first control that is not a Button is
found. if instead of

foreach( Button btn in myForm.Controls )

you write

foreach( Control btn in myForm.Controls )

you do not get that error

use this:
foreach( Button btn in myForm.Controls )
if ( btn is TextBox )
{
}


and take a look into OOP concepts as inheritance.

cheers,
 
Another way to code this:

foreach (Control ctl in myForm.Controls)
{
Button btn = ctl as Button;
if (btn != null)
{
...
}
}
 
Ignacio said:
Hi,


No, an exception is throw when the first control that is not a Button is
found. if instead of

foreach( Button btn in myForm.Controls )

you write

foreach( Control btn in myForm.Controls )

you do not get that error

use this:
foreach( Button btn in myForm.Controls )
if ( btn is TextBox )
{
}


and take a look into OOP concepts as inheritance.

cheers,

What I didn't understand is the way that the foreach loop works. I
thought it would implicitly only act on objects in the collection that
matched the declared type.
I understand that I could declare my foreach variable as a base Control,
but then I have to query each control's type. I thought the foreach
loop would preclude the extra code necessary on my part.
 
If you write foreach (Button btn in this.Controls)
it is expanded to code which does an explicit cast of each enumerated
element, so
(just for illustrative purpose)
Button btn = (Button)enumerator.Current;
and this rises exception when the Current Contol is not a Button
Hope this answers your question
 
foreach always iterates through every item in a collection, regardless
of what type you declare the receiving variable to be. For each item in
the collection, it attempts to cast that item to the type of variable
that you've supplied. If the cast fails, the foreach fails.

This is even clearer if you remember, as Lebesgue pointed out, that the
foreach loop just calls GetEnumerator() on the collection you pass it,
and the next item for each loop is just the result of calling
MoveNext() on the collection and then looking at the Current property.
You can "write" a foreach yourself like this:

IEnumerator en = myForm.Controls.GetEnumerator();
while (en.MoveNext())
{
Button btn = (Button)en.Current;
...
}

In theory, this should generate exactly the same code as

foreach (Button btn in myForm.Controls)
{
...
}

and, as you can see, the fact that the Current item is being cast to a
Button is separated from the call that moves to the next item, and,
indeed, the loop will fail on the line

Button btn = (Button)en.Current;

because for some controls en.Current will not be a Button.
 
foreach loops through every item in a collection, regardless of the
type of variable you use to receive the individual items. As Lebesgue
pointed out, foreach is implemented, under the covers using an
IEnumerator. You can write your own "foreach" loop as follows:

IEnumerator en = myForm.Controls.GetEnumerator();
while (en.MoveNext())
{
Button btn = (Button)en.Current;
...
}

this is exactly equivalent to

foreach (Button btn in myForm.Controls)
{
...
}

As you can see, the "moving to next control" call is completely
separate from the "get current item and cast to Button" call.
 
Hi,

What I didn't understand is the way that the foreach loop works. I
thought it would implicitly only act on objects in the collection that
matched the declared type.

I suggest you to read a book about C# , will help you understand the core of
the language and will save you a lot of problem down the line
 
I misunderstood how the foreach loop works. I thought it would make an
implicit type comparison on each object. That would save me from having
to check each base control object's type against my desired type in the
loop.
It's too bad I can't just write a foreach loop with my desired
descendant type and go from there...
 
Back
Top