views:

68

answers:

7

What does the following code do?

class Base { }
class Derived : Base { }
class Test
{
    void Foo(List<Base> list)
    {
        foreach (Derived obj in list)
        {
            // ...
        }
    }
}

I didn't expect it to even compile, but it does.

+6  A: 

Absolutely nothing, but it does it in a very inefficient manner.

The order of operations is this:

  1. Instantiate a new List, with zero items
  2. Iterate over that newly instantiated list, which has zero items
  3. Not cast the Base object to a Derived object, because the list has zero items. If the list had any items, this step would result in a runtime exception.
  4. Not execute the code in the foreach block, because the list has zero items.

Edit
Based on your edit, you run the risk of an InvalidCastException should any element of the list passed to Foo not actually be a Derived object.

Edit2
Why does it compile? Because foreach involves an implicit cast to Object for each item in the list, then another explicit cast to the specified type in the foreach block, in this case Derived

Randolpho
Right, thanks. Please change your answer to just say something like "Foreach attemts to silently downcast every item to Derived, and throws when that's impossible." - then it'll answer my question exactly, and I'll be able to accept.
Stefan Monov
@Stefan Monov: beat you to it by I think 3 seconds.
Randolpho
@Randolpho, I do not think your "Edit2" is correct. There is no implicit cast to object, there are explicit casts involved. First to the type defined in the collection (in this case, Base), then to the type defined in the foreach.
Anthony Pegram
0xA3's answer is more thorough, made it accepted. Thanks again.
Stefan Monov
@Stefan Monov: no problems, I agree 100%
Randolpho
A: 

Not sure why would not expect this to compile. Consider this, which is functionally equivalent:

{
  List<Base> list = new List<Base>();  // creates new, empty list of Base
  foreach (Derived obj in list)
  {
    // ...
  }
}

Does that make it clearer what's going on in your code?

EDIT re revised version.

Now it will throw InvalidCastException if your list contains anything that is not an instance of Derived. Derived 'is a' Base so still no issue with compiling this.

Steve Townsend
I wouldn't expect it to compile because normally you can't assign a Base object to a Derived variable without an explicit downcast - as in `Derived d = new Base()`
Stefan Monov
@Stefan - see @Jon Hanna's answer
Steve Townsend
A: 

It is equivalent to:

Enumerator<Base> enumerator = new List<Base>().GetEnumerator();
while (enumerator.MoveNext())
{
    Derived obj = (Derived) enumerator.Current;
    // ...
}

As your list is empty, the inner block does not execute.

Were it to contain elements, then there would be the risk of a InvalidCastException should any of the elements not be of type Derived.

Paul Ruane
If you want to be more accurate, there is actually a double cast going on. `Derived obj = (Derived)(Base)enumerator.Current;`
Anthony Pegram
I want to upvote you, but I can't. foreach does not use generic enumerators.
Randolpho
@Randolpho, C# spec 8.8.4: "If there is exactly one type T such that there is an implicit conversion from X to the interface System.Collections.Generic.IEnumerable<T>, then the collection type is this interface, the enumerator type is the interface System.Collections.Generic.IEnumerator<T>, and the element type is T."
Anthony Pegram
@Anthony Pegram: I cannot see any evidence of double casting from the non-optimized decompiled code, nor the IL code.
Paul Ruane
I've corrected the code with what I can observe in Reflector.
Paul Ruane
A: 

Technically, Randolpho is correct: your code does nothing. But I think you're getting at a different point.

Casting your list items to Derived will give you access to properties defined in the Derived class in addition to the properties defined in the Base class. However, you'll get errors if you have items not of type Derived in the list when you access those properties.

Barring some other need, you're better off having the list defined as List<Derived>.

Brad
+2  A: 

foreach includes a cast. Consider that it can be used with non-templated enumerations of objects. Casting from Base to Derived is valid, so the code is valid, but could throw an exception at runtime.

Jon Hanna
Ah, good, now I've learned another thing - I always thought that when I use a non-generic collection, I need to use a "System.Object" variable, like this: `foreach(object a in list)`. Now I know that it lets me put any type in the place of `object`, there.
Stefan Monov
A: 

the same as

list.Cast<Derived>();

it just type casting. in some case u might got error

888
+5  A: 

The behavior you are observing is according to section 8.8.4 The foreach statement of the C# language specification. This section defines the semantics of the foreach statement as follows:

[...] The above steps, if successful, unambiguously produce a collection type C, enumerator type E and element type T. A foreach statement of the form

foreach (V v in x) embedded-statement

is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            // here the current item will be casted                
            v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally {
        // Dispose e
    }
}

The reason that the compiler inserts this explicit cast is historic. C# 1.0 didn't have generics so in order to allow simple code like this

ArrayList list = new ArrayList();
list.Add(1);
foreach (int i in list)
{
   ...
}

it was decided to let the compiler introduce the cast.

0xA3