views:

424

answers:

3

Imagine the following code:

class foreach_convert
{
    public static void method2()
    {
        List<IComparable> x = new List<IComparable>();
        x.Add(5);

        foreach (string s in x)
        {
            //InvalidCastException in runtime
        }
    }
}

I wonder, why is this foreach behavior so... not C#-like? What happens here is an implicit cast to a subclass, which is error-prone and seems to be banned in every other place in the language. Or am I not right?

P.S. The reason I'm asking is that I had a bug in the similar code in my project, where I used to iterate through a custom Collection from an external library, which was called like SomeTypeCollection, but in fact provided a collection of base type items and could have contained items of SomeOtherType. My fault, but still neither the language, nor the compiler provided any explicit hints/warnings, which is quite unusual for C#...

+1  A: 

With C# 3 I'm using var - so I get the compiler warnings.

tanascius
Good point. Using var in foreach can save you from that bug, as it would detect the exact type of the collection you are iterating over.
Yacoder
+1  A: 

foreach works on IEnumerable, which returns objects of type object. For every item, the object will be cast to the type you provided.

Unless you use var in C# 3.0. Then the type will be taken from IEnumerable<T>, if it is implemented by the collection.

Philippe Leybaert
That gives the impression that foreach (int x in List<int>) would box and then unbox - which is doesn't. The C# compiler uses IEnumerable<T> in preference to IEnumerable where it's available.
Jon Skeet
you're right about the typed interface. Sometimes the C# specs are a little inconsistent: it seems that the foreach construct is the only thing in C# that allows covariance and contravariance.
Philippe Leybaert
+7  A: 

Think back to before generics... foreach had to cast so that you could do sensible things like:

foreach (string x in names)
{
    // ...
}

instead of:

foreach (object tmp in names)
{
    string x = (string) tmp;
    // ...
}

The latter is just icky, IMO. Providing an implicit cast is unlike the rest of the language, but makes it much easier to use in the vast majority of cases.

I suspect that if C# had had generics and extension methods to start with (so we could use OfType and Cast) that foreach wouldn't be specified in quite the same way.

Note that there's even more oddness in foreach: the type doesn't have to implement IEnumerable at all. So long as it has a GetEnumerator method which returns something which in turn has MoveNext() and Current, the C# compiler is happy. This meant you could implement a "strongly typed iterator" (to avoid boxing) before generics.

Jon Skeet
Right. I suspected thats a heritage from the very first C# and making it strongly-typed now is a breaking change... [grumble] still, there could have been a warning or just a note in MSDN :-)
Yacoder
Okay, more refreshed now, and capable of understanding... well... words... don't think my answer added anything that isn't said here :)
jerryjvl