Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objects of a specific type in foreach from an IEnumerable

I'm working with a legacy collection object that only implements non-generic IEnumerable and ICollection. What exactly happens with this object when I try to use this object with a foreach giving a more specific type on the LHS of the foreach expression?

// LegacyFooCollection implements non-generic IEnumerable
LegacyFooCollection collection = GetFooCollection();
foreach (Foo f in collection)
{
    // etc.
}

I know (because I've tried it) that this is safe when everything in collection really is of type Foo, but what happens if that fails?

like image 770
JSBձոգչ Avatar asked Aug 23 '11 18:08

JSBձոգչ


2 Answers

The C# compiler performs the cast implicitly for you. In terms of the casting (but only in those terms1) it's equivalent to:

foreach (object tmp in collection)
{
    Foo f = (Foo) tmp;
    ...
}

Note that this will happen with generic collections too:

List<object> list = new List<object> { "hello", "there", 12345 };

// This will go bang on the last element
foreach (string x in list) 
{
}

This is all detailed in section 8.8.4 of the C# 4 spec.

If you're using .NET 3.5 or higher and you want to only select items of the appropriate type, you can use Enumerable.OfType:

LegacyFooCollection collection = GetFooCollection();
foreach (Foo f in collection.OfType<Foo>())
{
    // etc.
}

That may not be necessary for a LegacyFooCollection, but it can be useful when you're trying to find (say) all the TextBox controls in a form.


1 The differences are:

  • In your original code, f is read-only; in the "conversion" it's writable
  • In your original code, if you capture f you will (currently) capture a single variable across all iterations, as opposed to a separate variable per iteration in the "conversion"
like image 187
Jon Skeet Avatar answered Oct 21 '22 21:10

Jon Skeet


This code will create a runtime type conversion (cast) of each element of the enumerable to Foo. So, if you did foreach (string f in collection) you would get a ClassCastException at runtime, the first time it tries to convert a Foo reference to a String reference.

like image 24
cdhowie Avatar answered Oct 21 '22 21:10

cdhowie