foreach doesn't work with array of bools?

S

Stoitcho Goutsev \(100\) [C# MVP]

I'm not entirely sure about that - I don't think it's the C# compiler
which generates different IL code, but the JIT which generates
different native code. In the case of foreach, however, it's the C#
compiler generating different code.

It could be JIT as well. but the c# compilers also generates different code.

CIL has special instructions for working with arrays for example *ldlen* for
reading array length, indexing is also done in a different way and so on.
Do you mean implementing IEnumerable in a strongly-typed way as well as
with the normal "object" version (which would be explicitly
implemented)? Not sure what you mean by CIL compliant here - did you
mean CLS compliant? If so, which bit isn't CLS compliant? (I'm not
familiar with all the CLS rules - is it having two methods which differ
only in return type?)
Ofcourse CLS not CIL :)

I mean not implementing IEnumerable at all :)
The code that C# compilers genrates doesn't use neither IEnumberable nor
IEnumerator for the *foreach* loop. However in order the code to be CLS
compliant collections have to implement IEnumerable and the enumerator
object has to implement IEnumerator interface. One can take an advantage by
implementing explicitly those interfaces and provide strongly typed
enumeration as well.

To back up my words try the following
It's a silly collection of 3 integers, but there is no boxing.

------------------------

using System;

namespace ConsoleApplication
{

class Foo
{
int a;
int b;
int c;

//Class for the enumerator
public class MyOwnEnumerator
{
int index = -1;
Foo collection;
public int Current
{
get
{
if(index < 0 || index > 2)
throw new InvalidOperationException("XXX");
int res = 0;
switch(index)
{
case 0:
res = collection.a;
break;
case 1:
res = collection.b;
break;
case 2:
res = collection.c;
break;
}

return res;
}
}


public MyOwnEnumerator(Foo coll)
{
this.collection = coll;
}

public void Reset()
{
index = -1;
}

public bool MoveNext()
{
index++;
if(index > 2)
return false;

return true;
}
}


public Foo(int i0, int i1, int i2)
{
a = i0;
b = i1;
c = i2;

}
public MyOwnEnumerator GetEnumerator()
{
return new MyOwnEnumerator(this);

}
}



class Class1
{
[STAThread]
static void Main(string[] args)
{
Foo foo = new Foo(100, 200, 300);

foreach(int i in foo)
{
Console.WriteLine(i);
}
}

}
}
 
J

Joe Rattz

Ok, I have done some testing. I feel like everyone is saying the the
variable 'bit' in my example is just a local copy. Is this because it is a
primitive type? Because when I try the same thing with a more complex data
type, when I call a method on the local object, it updates the object in the
array too! Here's the code.

System.Text.StringBuilder[] a = new
System.Text.StringBuilder[10];
// This is just to initialize the objects
for(int i = 0; i < 10; i++)
{
a = new System.Text.StringBuilder("Joe");
}

foreach(System.Text.StringBuilder s in a)
{
s.Append(" was here");
int i = 5; // This is just to let me
stop inside the debugger and still be in scope for s.
}

In the code above I created an array of StringBuilder objects. First loop
(for) initializes them all. Second loop (foreach) calls the Append method
which should change the value stored in the local object, which it does.
But, the element in the array is updated too indicating that this local
object is indeed a reference to the original element in the array.

To me, it seems that if the type stored in the array is simple (primitive
such as bool, int, etc.) then the element is treated as a value type and is
a local copy. But, if the type is more complex like a class, then it is a
reference type. None of this surprises me, except why haven't I found this
documented?

Also, I just changed the last loop to try to assign the local variable when
it is a reference type and it too is read-only. Here is what I changed the
last loop to:

foreach(System.Text.StringBuilder s in a)
{
s = new System.Text.StringBuilder("Joe was here");
int i = 5;
}

So, regardless of value or reference type, I can't reassign the local
variable. But, it seems inconsistent that I can change a member of the
variable if it is a reference type but I cannot if it is a value type. I
don't even want to think about if instead of using bool type I start using
System.Boolean!

I did just find this though which would have answered my initial question:

foreach (type identifier in expression) statement
where:
...
identifier
The iteration variable that represents the collection element. If
the iteration variable is a value type, it is effectively a read-only
variable that cannot be modified.

That certainly tells me that the variable is read-only and confirms what the
compiler is telling me.

Also from that same help it says "foreach ... but should not be used to
change the contents of the collection to avoid unpredictable side effects.".
This explains why they made the variable read-only.

What I feel like I am missing is anything in the syntax that would indicate
that 'bit' is read-only. Or that sometimes it is a value type and sometimes
it is a reference type. How is a developer supposed to know or infer this
any way other than stumbling over it like I did?

Thanks.
 
J

Jon Skeet [C# MVP]

Stoitcho Goutsev (100) said:
It could be JIT as well. but the c# compilers also generates different code.
CIL has special instructions for working with arrays for example *ldlen* for
reading array length, indexing is also done in a different way and so on.

Sure - but I don't believe it does the optimisation (in terms of only
checking the length once, etc) in the JIT. Yes, it uses array access
Ofcourse CLS not CIL :)

I mean not implementing IEnumerable at all :)
The code that C# compilers genrates doesn't use neither IEnumberable nor
IEnumerator for the *foreach* loop. However in order the code to be CLS
compliant collections have to implement IEnumerable and the enumerator
object has to implement IEnumerator interface. One can take an advantage by
implementing explicitly those interfaces and provide strongly typed
enumeration as well.

To back up my words try the following
It's a silly collection of 3 integers, but there is no boxing.

Yup - but which CLS rule does it break? And is there an advantage in
not also implementing IEnumerable/IEnumerator?
 
J

Joe Rattz

Got nothing to do with bool, with int[] it would be the same.

Yeah, I discovered that after I posted about the bool.
foreach identifier:
The iteration variable that represents the collection element. If the
iteration variable is a value type, it is effectively a read-only variable
that cannot be modified.

I just stumbled on that too. I feel like the syntax is poor though since I
can't tell it is read-only.


Sam Jost said:
Joe Rattz said:
bool[] bits = new bool[10];
foreach(bool bit in bits)
{
bit = false;
}

The compiler complains on the "bit = false;" stating that bit is
read-only.

Jup. the iterator variable of foreach is readonly.
Got nothing to do with bool, with int[] it would be the same.

from the c# programmers reference:

foreach identifier:
The iteration variable that represents the collection element. If the
iteration variable is a value type, it is effectively a read-only variable
that cannot be modified.

Sam
 
M

Mike Schilling

Joe Rattz said:
Ok, I have done some testing. I feel like everyone is saying the the
variable 'bit' in my example is just a local copy. Is this because it is a
primitive type? Because when I try the same thing with a more complex data
type, when I call a method on the local object, it updates the object in the
array too!

Certainly. The array contains references to objects, so your loop variable
contains a copy of the reference to the object, not a copy of the object
itself (whatever that would mean). Just as with value types, you can't use
the loop variable to update the array.
 
J

Jon Skeet [C# MVP]

Joe Rattz said:
Ok, I have done some testing. I feel like everyone is saying the the
variable 'bit' in my example is just a local copy. Is this because it is a
primitive type?

No, not really.
Because when I try the same thing with a more complex data
type, when I call a method on the local object, it updates the object in the
array too! Here's the code.

System.Text.StringBuilder[] a = new
System.Text.StringBuilder[10];
// This is just to initialize the objects
for(int i = 0; i < 10; i++)
{
a = new System.Text.StringBuilder("Joe");
}

foreach(System.Text.StringBuilder s in a)
{
s.Append(" was here");
int i = 5; // This is just to let me
stop inside the debugger and still be in scope for s.
}


There you're not changing the value of s - you're changing the data
within the object that s refers to.
In the code above I created an array of StringBuilder objects. First loop
(for) initializes them all. Second loop (foreach) calls the Append method
which should change the value stored in the local object, which it does.
But, the element in the array is updated too indicating that this local
object is indeed a reference to the original element in the array.

The array contents aren't changed in any way. The array does *not*
contain the object itself - only references to the object.
To me, it seems that if the type stored in the array is simple (primitive
such as bool, int, etc.) then the element is treated as a value type and is
a local copy. But, if the type is more complex like a class, then it is a
reference type. None of this surprises me, except why haven't I found this
documented?

Um, it's documented all over the place. It's nothing to do with foreach
- it's how reference types and value types work all over the place.
Structs are value types, classes are reference types. int, bool, char
etc are value types, as documented in section 8.2.1 of the C# spec
(ECMA numbering).
Also, I just changed the last loop to try to assign the local variable when
it is a reference type and it too is read-only. Here is what I changed the
last loop to:

foreach(System.Text.StringBuilder s in a)
{
s = new System.Text.StringBuilder("Joe was here");
int i = 5;
}

So, regardless of value or reference type, I can't reassign the local
variable.
Indeed.

But, it seems inconsistent that I can change a member of the
variable if it is a reference type but I cannot if it is a value type.

You're not changing a member of the variable. You're changing a member
of the object the value of the variable refers to. That doesn't involve
changing the value of the variable itself.
I don't even want to think about if instead of using bool type I start using
System.Boolean!

There'll be no difference - they're exactly the same thing.
I did just find this though which would have answered my initial question:

foreach (type identifier in expression) statement
where:
...
identifier
The iteration variable that represents the collection element. If
the iteration variable is a value type, it is effectively a read-only
variable that cannot be modified.

It's not "effectively" a read-only variable. It *is* a read-only
variable - and not only if it's a value type.
That certainly tells me that the variable is read-only and confirms what the
compiler is telling me.

Also from that same help it says "foreach ... but should not be used to
change the contents of the collection to avoid unpredictable side effects.".
This explains why they made the variable read-only.

Not really - changing the value of the variable wouldn't be changing
the collection anyway.
What I feel like I am missing is anything in the syntax that would indicate
that 'bit' is read-only. Or that sometimes it is a value type and sometimes
it is a reference type. How is a developer supposed to know or infer this
any way other than stumbling over it like I did?

By reading the specification, perhaps? (Section 15.8.4 in the spec.)
It's clearly documented that the declared variable is read-only. As for
"sometimes it is a value type and sometimes it is a reference type" -
it's whatever type you declare it to be. If you declare it to be a
reference type, it's a reference type. If you declare it to be a value
type, it's a value type.
 
J

Jon Skeet [C# MVP]

Joe Rattz said:
I just stumbled on that too. I feel like the syntax is poor though since I
can't tell it is read-only.

You can if you know the language though. It's clearly documented - it's
not hard to read the spec in this case (compared with, say,
overloading).

Do you think the syntax is poor that declaring a member variable
without specifying an access modifier is the same as declaring it to be
private?
 
J

Joe Rattz

Or that sometimes it is a value type and sometimes it is a reference type.

As a followup to my previous post, were this C++, this behavior would not
surprise me. I am used to primitives being passed by value and objects
being passed by reference. But since C# is supposed to default to pass by
value, I would expect it to be consistent when using the foreach loop.

Joe Rattz said:
Ok, I have done some testing. I feel like everyone is saying the the
variable 'bit' in my example is just a local copy. Is this because it is a
primitive type? Because when I try the same thing with a more complex data
type, when I call a method on the local object, it updates the object in the
array too! Here's the code.

System.Text.StringBuilder[] a = new
System.Text.StringBuilder[10];
// This is just to initialize the objects
for(int i = 0; i < 10; i++)
{
a = new System.Text.StringBuilder("Joe");
}

foreach(System.Text.StringBuilder s in a)
{
s.Append(" was here");
int i = 5; // This is just to let me
stop inside the debugger and still be in scope for s.
}

In the code above I created an array of StringBuilder objects. First loop
(for) initializes them all. Second loop (foreach) calls the Append method
which should change the value stored in the local object, which it does.
But, the element in the array is updated too indicating that this local
object is indeed a reference to the original element in the array.

To me, it seems that if the type stored in the array is simple (primitive
such as bool, int, etc.) then the element is treated as a value type and is
a local copy. But, if the type is more complex like a class, then it is a
reference type. None of this surprises me, except why haven't I found this
documented?

Also, I just changed the last loop to try to assign the local variable when
it is a reference type and it too is read-only. Here is what I changed the
last loop to:

foreach(System.Text.StringBuilder s in a)
{
s = new System.Text.StringBuilder("Joe was here");
int i = 5;
}

So, regardless of value or reference type, I can't reassign the local
variable. But, it seems inconsistent that I can change a member of the
variable if it is a reference type but I cannot if it is a value type. I
don't even want to think about if instead of using bool type I start using
System.Boolean!

I did just find this though which would have answered my initial question:

foreach (type identifier in expression) statement
where:
...
identifier
The iteration variable that represents the collection element. If
the iteration variable is a value type, it is effectively a read-only
variable that cannot be modified.

That certainly tells me that the variable is read-only and confirms what the
compiler is telling me.

Also from that same help it says "foreach ... but should not be used to
change the contents of the collection to avoid unpredictable side effects.".
This explains why they made the variable read-only.

What I feel like I am missing is anything in the syntax that would indicate
that 'bit' is read-only. Or that sometimes it is a value type and sometimes
it is a reference type. How is a developer supposed to know or infer this
any way other than stumbling over it like I did?

Thanks.
 
S

Stoitcho Goutsev \(100\) [C# MVP]

I mean not implementing IEnumerable at all :)
Yup - but which CLS rule does it break? And is there an advantage in
not also implementing IEnumerable/IEnumerator?

Yeah, it's been a while since we heven't argued. I forgot that you are all
about specs :) No offence I admire that.

You are probably right. I took a brief look at the docs and didn't find
anything that says not having IEnumerable is not CLS-compliant. I read an
article in MSDN magazine and the author claimed so and I took it for
checked.
Anyways I tried wint VB.NET it works just fine.
 
J

Jon Skeet [C# MVP]

Joe Rattz said:
As a followup to my previous post, were this C++, this behavior would not
surprise me. I am used to primitives being passed by value and objects
being passed by reference. But since C# is supposed to default to pass by
value, I would expect it to be consistent when using the foreach loop.

It *is* being consistent. You just need to understand that "pass
reference by value" is different to "pass object by reference".

See http://www.pobox.com/~skeet/csharp/parameters.html
 
J

Joe Rattz

You just need to understand that "pass
reference by value" is different to "pass object by reference".

I was unclear on this. The link helped a lot. Thanks!
 
A

Adam Clauss

Actually, no - variable "i" is changed by the i++ part. As it's a for
loop, that's fine - it's only foreach that makes the variable readonly.
Your right, I worded that very badly, sorry.

If you don't trust that part of the spec, how do you decide which parts
to trust and which parts not to?
Hehe, I guess it is not so much a trust issue as a habit issue. In the time I would spend "thinking" about "oh, hey, I don't have
to initialize this" I could have just already done it anyway :)
 
J

Jon Skeet [C# MVP]

Hehe, I guess it is not so much a trust issue as a habit issue. In
the time I would spend "thinking" about "oh, hey, I don't have to
initialize this" I could have just already done it anyway :)

Then change your habits :)

Seriously, although you only have to think about it once when coding
it, the computer will have to go through that initialization each time.
In many cases that won't be an issue - but sometimes it will. If you
can get into the habit of avoiding writing unnecessary code, your
programs will be more readable (as the useful code doesn't have to be
weeded out from the useless code) and more efficient (as the
initialization will only be done once).

It's always a pain to get out of habits from one language when writing
in a new one - but it's something to make sure you do. Some of the
worst code I've seen (in Java and C#) has been from people trying to
take the idioms of one language and put them in another. Yuk.
 

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