foreach doesn't work with array of bools?

J

Joe Rattz

Hmmm, I wrote the following code. I want an array of bools and I want to
intialize them to false.

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.
If I iterate using an index though, it compiles just fine:

for(int i = 0; i < bits.Length; i++)
{
bits = false;
}

That compiles just fine. What is going on?

Also, the array of bools seems to have intialized to false anyway. Can I
count on that? Should I even worry about initializing them?

Thanks.
 
J

Julie

Joe said:
Hmmm, I wrote the following code. I want an array of bools and I want to
intialize them to false.

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.
If I iterate using an index though, it compiles just fine:

for(int i = 0; i < bits.Length; i++)
{
bits = false;
}

That compiles just fine. What is going on?

Also, the array of bools seems to have intialized to false anyway. Can I
count on that? Should I even worry about initializing them?


The default value for a constructed bool is 'false':

http://msdn.microsoft.com/library/en-us/csref/html/vcrefdefaultconstructors.asp

An alternative method of alloc and initialize is:

bool[] bits = new bool[] { false, false, false, false /*etc*/ };

This allows you to specify differing values in construction, such as:

bool[] bits = new bool[] { true, false, true, false /*etc*/ };
 
M

Marc Jennings

bool[] bits = new bool[10];
foreach(bool bit in bits)
{
bit = false;
}

In this snippet, "bit" is a pointer to one of the values in the bits
array. It is a read-only pointer, though, so you cannot write to it.
for(int i = 0; i < bits.Length; i++)
{
bits = false;
}


This makes things easier to deal with. Basically, you are setting the
value of an array member directly in this snippet, rather than trying
to access it through a pointer. The read/write nature of the array
you created is therefore preserved.

HTH
 
N

Nicholas Paldino [.NET/C# MVP]

Joe,

The compiler is right. When iterating using a foreach, the variable
declared in the loop is read-only for the scope of the loop, only the
enumerator can assign to it.

As for assigning to the array itself when iterating yourself, that's a
different operation you are performing, you are modifying the array, not the
value that stores the current value in the enumeration.

Hope this helps.
 
M

Mattias Sjögren

Joe,
What is going on?

Exactly what the compiler says. The variable in a foreach statement is
read only.

Also, the array of bools seems to have intialized to false anyway. Can I
count on that?

Yes you can. When you create a new array, all elements have their
default initial value. For bool, that's false.

Should I even worry about initializing them?

I wouldn't waste CPU cycles on it.



Mattias
 
S

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

In addition to waht the others said I'd like to remind you that bool is a
value type and there is boxing/unboxing going on so even if *bit* wasn't
read-only you probably wouldn't be able to change the value saved in the
array anyways.
 
A

Adam Clauss

I actually just discovered this today actually hehe..

What is happening is the variable declared in the foreach becomes readonly.
So, in the case of
foreach(bool bit in bits)
the variable bit CANNOT have its value changed. It can be read from, but not changed. Which is why:
for(int i = 0; i < bits.Length; i++)
works. On that example, the variable i only gets read from (to tell bits[] which index to use).

That make sense?

I believe in .net that all variables have a default value... but I come from a C++ background, so I don't completely trust it and I
tend to always initialize all my variables anyway.
 
J

Jon Skeet [C# MVP]

Stoitcho Goutsev (100) said:
In addition to waht the others said I'd like to remind you that bool is a
value type and there is boxing/unboxing going on so even if *bit* wasn't
read-only you probably wouldn't be able to change the value saved in the
array anyways.

No, there's no boxing going on. Try compiling this code and then
disassembling it - you'll see there's no boxing:

using System;

class Test
{
static void Main()
{
bool[] bits = new bool[5];
int x=0;
foreach (bool bit in bits)
{
if (bit)
{
x++;
}
}
Console.WriteLine (x);
}
}

foreach on arrays works slightly differently to how it works on other
types.
 
J

Joe Rattz

The default value for a constructed bool is 'false':

Great, thanks!

An alternative method of alloc and initialize is:
bool[] bits = new bool[] { false, false, false, false /*etc*/ };

Thanks. I was already aware of that, but that won't work in my case because
in my real code (not the simplified version I posted here), the number of
elements is a variable.


Julie said:
Joe said:
Hmmm, I wrote the following code. I want an array of bools and I want to
intialize them to false.

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.
If I iterate using an index though, it compiles just fine:

for(int i = 0; i < bits.Length; i++)
{
bits = false;
}

That compiles just fine. What is going on?

Also, the array of bools seems to have intialized to false anyway. Can I
count on that? Should I even worry about initializing them?


The default value for a constructed bool is 'false':

http://msdn.microsoft.com/library/en-us/csref/html/vcrefdefaultconstructors.asp

An alternative method of alloc and initialize is:

bool[] bits = new bool[] { false, false, false, false /*etc*/ };

This allows you to specify differing values in construction, such as:

bool[] bits = new bool[] { true, false, true, false /*etc*/ };
 
J

Joe Rattz

I believe in .net that all variables have a default value... but I come
from a C++ background, so I don't completely trust it and I
tend to always initialize all my variables anyway.

Same here. But since Julie (thanks Julie!) included the link to the
language definition, I feel comfortable trusting it in this case.


Adam Clauss said:
I actually just discovered this today actually hehe..

What is happening is the variable declared in the foreach becomes readonly.
So, in the case of
foreach(bool bit in bits)
the variable bit CANNOT have its value changed. It can be read from, but not changed. Which is why:
for(int i = 0; i < bits.Length; i++)
works. On that example, the variable i only gets read from (to tell bits[] which index to use).

That make sense?

I believe in .net that all variables have a default value... but I come
from a C++ background, so I don't completely trust it and I
tend to always initialize all my variables anyway.

--
Adam Clauss
(e-mail address removed)

Hmmm, I wrote the following code. I want an array of bools and I want to
intialize them to false.

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.
If I iterate using an index though, it compiles just fine:

for(int i = 0; i < bits.Length; i++)
{
bits = false;
}

That compiles just fine. What is going on?

Also, the array of bools seems to have intialized to false anyway. Can I
count on that? Should I even worry about initializing them?

Thanks.

 
J

Jon Skeet [C# MVP]

Adam Clauss said:
I actually just discovered this today actually hehe..

What is happening is the variable declared in the foreach becomes readonly.
So, in the case of
foreach(bool bit in bits)
the variable bit CANNOT have its value changed. It can be read from,
but not changed. Which is why: for(int i = 0; i < bits.Length; i++)
works. On that example, the variable i only gets read from (to tell
bits[] which index to use).

That make sense?

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.
I believe in .net that all variables have a default value... but I
come from a C++ background, so I don't completely trust it and I tend
to always initialize all my variables anyway.

If you don't trust that part of the spec, how do you decide which parts
to trust and which parts not to?
 
J

Joe Rattz

Hmmm, I thought (safe) C# didn't have pointers. So, I assume you really
meant reference? And I assume its a read-only reference?

I guess what I am wondering is, is it only a case that the reference itself
is read-only meaning I cannot assign another entire object of the type to
the variable? Or does it mean the entire object referenced by the variable
is read-only?

In other words, lets assume I had an array of objects that have set methods
in them. Can I call the set methods? If so, then the object is not really
read-only.




Marc Jennings said:
bool[] bits = new bool[10];
foreach(bool bit in bits)
{
bit = false;
}

In this snippet, "bit" is a pointer to one of the values in the bits
array. It is a read-only pointer, though, so you cannot write to it.
for(int i = 0; i < bits.Length; i++)
{
bits = false;
}


This makes things easier to deal with. Basically, you are setting the
value of an array member directly in this snippet, rather than trying
to access it through a pointer. The read/write nature of the array
you created is therefore preserved.

HTH
 
J

Jon Skeet [C# MVP]

Joe Rattz said:
Hmmm, I thought (safe) C# didn't have pointers. So, I assume you really
meant reference? And I assume its a read-only reference?

It's neither a pointer nor a reference. It's just a variable. The value
of the variable is assigned at the start of each iteration of the loop.
I guess what I am wondering is, is it only a case that the reference itself
is read-only meaning I cannot assign another entire object of the type to
the variable? Or does it mean the entire object referenced by the variable
is read-only?

It's just the variable itself.
In other words, lets assume I had an array of objects that have set methods
in them. Can I call the set methods? If so, then the object is not really
read-only.

Yes, you can call the set methods. No, the object itself is not read-
only - the variable is.
 
P

Paul E Collins

Joe Rattz said:
I guess what I am wondering is, is it only a case that
the reference itself is read-only meaning I cannot assign
another entire object of the type to the variable?

Yes, that's right.
In other words, lets assume I had an array of objects
that have set methods in them. Can I call the set methods?

Yes. You can set the properties of the referenced object in a foreach
loop, but you cannot change the target of the reference, i.e.
'replace' the object.

P.
 
S

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

No, there's no boxing going on. Try compiling this code and then
disassembling it - you'll see there's no boxing:


Yup, I wasn't clear enough. I was talking in common when collections and
value types are involved. However, *bit* is a is actually declared as a
local varaible and as such it receives a copy of value-type array's elements
(no boxing as you said). Changing *bit*, if it wasn't read only, would
change that local copy not the element in the array. So it is different form
using the indexer.


foreach on arrays works slightly differently to how it works on other
types.

When it goes to arrays the compiler generates different code not only for
*foreach*, but for others language constructions as well. It can do more
optimization because of the strongly typed nature of the arrays and the
constness of the array's length.

However, even custom made collections can be made in a way that *foreach*
doesn't do boxing, but it won't be CIL compliant. That's why I made this
remark.
But, yes, *foreach* with arrays doesn't cause boxing.
 
J

Jon Skeet [C# MVP]

Stoitcho Goutsev (100) said:
Yup, I wasn't clear enough. I was talking in common when collections and
value types are involved.

Yes, in things like ArrayList there would certainly be boxing.
However, *bit* is a is actually declared as a
local varaible and as such it receives a copy of value-type array's elements
(no boxing as you said). Changing *bit*, if it wasn't read only, would
change that local copy not the element in the array. So it is different form
using the indexer.
Indeed.


When it goes to arrays the compiler generates different code not only for
*foreach*, but for others language constructions as well. It can do more
optimization because of the strongly typed nature of the arrays and the
constness of the array's length.

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.
However, even custom made collections can be made in a way that *foreach*
doesn't do boxing, but it won't be CIL compliant. That's why I made this
remark.

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?)
But, yes, *foreach* with arrays doesn't cause boxing.

:)
 
M

Mike Schilling

Jon Skeet said:

The loop in question:

bool[] bits = new bool[10];
foreach(bool bit in bits)
{
bit = false;
}

It's neither a pointer nor a reference. It's just a variable. The value
of the variable is assigned at the start of each iteration of the loop.


It's just the variable itself.

And, I presume, the variable is read-only to prevent you from thinking that
this loop changed the array's values, when in fact all it would do (if
legal) is the moral equivalent of:

bool[] bits = new bool[10];
for (int i = 0; i < 10 ; i++) {
bool bit = bits;
bit = false; // useless, since "bit" is about to
go out of scope anyway
}
 
M

Mike Schilling

Jon Skeet said:
If you don't trust that part of the spec, how do you decide which parts
to trust and which parts not to?

By looking at the compiler's regression test suite :)
 
J

Jon Skeet [C# MVP]

Mike Schilling said:
It's just the variable itself.

And, I presume, the variable is read-only to prevent you from thinking that
this loop changed the array's values, when in fact all it would do (if
legal) is the moral equivalent of:

bool[] bits = new bool[10];
for (int i = 0; i < 10 ; i++) {
bool bit = bits;
bit = false; // useless, since "bit" is about to
go out of scope anyway
}


Possibly. It's hard to say for sure why it's readonly, to be honest.
 
S

Sam Jost

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
 

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