Implicit unsafe casting in foreach? What gives?

  • Thread starter Thread starter james
  • Start date Start date
J

james

Hey Guys,

Would anyone mind explaining why a foreach will implicitly do unsafe
casts, and if there is a way to turn this off?

Here is an example:

ulong[] vals = new ulong[] { ulong.MaxValue };
foreach (uint x in vals)
Console.WriteLine(x);

Thanks,
James
 
James,

Unfortunately, no. From the language specification:

The type of the expression of a foreach statement must be a collection type
(as defined below), and an explicit conversion (§?6.2) must exist from the
element type of the collection to the type of the iteration variable. If
expression has the value null, a System.NullReferenceException is thrown.



Since an explicit conversion exists between ulong and uint, the compiler
is right in this case.



You can get around it yourself, though, by iterating through the
elements manually, and then performing the check of the returned type vs the
type you wish to use.



Hope this helps.
 
james said:
Would anyone mind explaining why a foreach will implicitly do unsafe
casts, and if there is a way to turn this off?

Here is an example:

ulong[] vals = new ulong[] { ulong.MaxValue };
foreach (uint x in vals)
Console.WriteLine(x);

The reason why the foreach does an unsafe cast, is because it doesn't
know otherwise. A foreach can be done on any object that implements
IEnumerable, and IEnumerable returns a GetEnumerator which provides an
IEnumerator which has a method "Current" that returns Object. Therefore,
foreach always thinks that it is dealing with Object, so it always has to do
an unsafe cast regardless of the type of variable that you use to control
the loop. That is, the compiler translates the previous foreach to something
similar to the following:

IEnumerator e = vals.GetEnumerator();
while (e.MoveNext())
{
object obj = e.Current();
uint x= (uint) obj;
Console.WriteLine(x);
}

There is an IEnumerable<T> that solves the preceding problem. However,
the documentation of System.Array only shows arrays to implement
IEnumerable, and not IEnumerable<typeoftheelements>.
 
Tyepd arrays do actually implement IEnumerable<T>. I'm not sure how
to prove this with the documentation, object browser, or Reflector,
but the following code demonstrates it:

private static void Test<T>(IEnumerable<T> thingToEnumerate)
{
int[] ints = null;
Test(ints); // works

Array array = null;
Test(array); // doesn't work
}

Also technically a collection does not need to implement IEnumerable
to work with foreach--it only needs a GetEnumerator() method. It's
technically possible to write a class that has GetEnumerator() and
doesn't implement IEnumerable and it will still work with
foreach--although you never want to, just a silly little technicality.

Sam
 
Alberto Poblacion <[email protected]>
wrote:

The reason why the foreach does an unsafe cast, is because it doesn't
know otherwise. A foreach can be done on any object that implements
IEnumerable, and IEnumerable returns a GetEnumerator which provides an
IEnumerator which has a method "Current" that returns Object. Therefore,
foreach always thinks that it is dealing with Object, so it always has to do
an unsafe cast regardless of the type of variable that you use to control
the loop.

That's not quite true. This doesn't compile, for instance:

using System;

class Test
{
static void Main()
{
int[] array = new int[10];
foreach (string x in array)
{
Console.WriteLine (x);
}
}
}

and using generics, things get better even for collections:

using System;
using System.Collections.Generic;
using System.IO;

class Test
{
static void Main()
{
List<Stream> list = new List<Stream>();
foreach (string x in list)
{
Console.WriteLine (x);
}
}
}
 
Samuel R. Neff said:
Tyepd arrays do actually implement IEnumerable<T>. I'm not sure how
to prove this with the documentation,

citation from the specs (C#2.0 ECMA):
A one-dimensional array S[] implements the interface
System.Collections.Generic.IList<S>
(IList<S> for short) and its base interfaces.

The spec of C#1.0 shall have an analog sentence without generics.

Christof
 

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

Back
Top