views:

257

answers:

2

I'm trying to upcast a parameter passed as an object (which is an upcast of an object of type IEnumerable) to IEnumerable where Foo implements IFoo.

Here is an example of what I'd like to do but it does not work.

public void F(object o)
{
    //I know the object o is of type IEnumerable<Foo> where Foo implements IFoo
    IEnumerable<IFoo> ifoos = (IEnumerable<IFoo>) o);
}

Is there a work around? I don't want to make the function F Foo specific but I cannot get it to cast to the Interface Unless I do:

IEnumerable<IFoo> ifoos = (IEnumerable<Foo>) o).Select( f => (IFoo) f);

Thanks

Giuseppe

+9  A: 

Before .NET 4.0, you can't - IEnumerable<T> is invariant before .NET 4.0.

With .NET 4.0, the code you've posted will work fine.

In .NET 3.5 you could use Enumerable.Cast:

public void F(object o)
{
    IEnumerable<IFoo> ifoos = ((IEnumerable) o).Cast<IFoo>();
}

Basically this uses the weakly typed IEnumerable (which both IEnumerable<Foo> and IEnumerable<IFoo> extend, so the cast will be fine) and then applies the Cast<T> method, which will basically chain another iterator to perform a cast on each item. That cast will certainly work if Foo : IFoo, so you'll be fine.

In .NET 2.0 you'd have to basically write Cast yourself, which is easy enough - particularly if you don't need to do any shortcuts:

public static IEnumerable<TResult> Cast<TSource, TResult>
    (IEnumerable<TSource> source)
{
    foreach(TSource item in source)
    {
        yield return (TResult) (object) item;
    }
}

The double cast is slightly awkward, but it works...

Jon Skeet
More info on covariance/contravariance in .NET 4: http://msdn.microsoft.com/en-us/library/dd233054%28VS.100%29.aspx and related topics in the table at the bottom.
Julien Lebosquain
Or by the early access 2nd edition of C# in Depth and read chapter 13 :) (http://manning.com/skeet2)
Jon Skeet
Incredibly quick reply, spot on THANK YOU!
Giuseppe R
Sorry, that should be "buy" not "by" obviously :)
Jon Skeet
+1  A: 

Would a generic method be acceptable?

interface IFoo { }
class Foo : IFoo { }
static void F<T>(IEnumerable<T> data) where T : IFoo
{ 
    foreach(T item in data) {
       // compiler knows that `item` is an `IFoo` etc
    }
}

...
List<Foo> foos = new List<Foo>();
F(foos);

Otherwise; wait until .NET 4.0 / C# 4.0 / VS2010.

Marc Gravell
It would if I were creating the function for scratch but I'm tweaking the body of an existing function so that it can handle other types that implement IFoo.
Giuseppe R