views:

283

answers:

7

I am sure I am missing something simple, however I am trying to convert a strongly typed list of objects that all implement an interface in to a list of that interface type.

Below is a sample to demonstrate the error:

public void ExampleCode(){
    List<Cube> cubes = new List<Cube>();
    List<Shape> allShapes;
    allShapes = cubes;//Syntax Error
    allShapes = (List<Shape>)cubes;//Syntax Error  
}

public class Cube : Shape
{
    public int ID { get; set; }
    public int Sides { get; set; }
}

public interface Shape
{
  int ID { get; set; }
  int Sides { get; set; }
}
+7  A: 

Instead of casting like that, try:

allShapes = cubes.Cast<Shape>().ToList();

You need .NET 3.5 for this. I believe the Cast extension method can be found in System.Linq.

Dave Markle
You beat me to it.
Steven
Note, this copies the list (albeit if it contains references the references and not the objects will be copied). This is not the same as casting the list object itself.
Richard
@Richard: Great point.
Dave Markle
This is as close as you can get in .NET 3.5. There's no way to cast the list object as asked.
Lasse V. Karlsen
+5  A: 

You can't. Because List<T> and ILIst<T> to only support invariant type parameters. This is down to T being both use for input and output parameters (e.g. return values). Otherwise you can break the type safety.

Other interfaces (e.g. IEntumerable<T>) do allow some variance.

See Eric Lippert's blog "Fabulous Adventures In Coding" for discussion of contra- and co-variance. Specifically the "Covariance and Contravariance" tag.

Edit, just added to the "C# Frequently Asked Questions" blog: Covariance and Contravariance FAQ

Richard
+2  A: 

You cannot do this since by casting this way you can potentially lose all type safety. For instnce, casting List<Shape> to List<object> will result in that objects of any type can be added to the list, which will be downright inconsistent.

Anton Gogolev
A: 

You can also do:

allShapes = cubes.ConvertAll(x => (Shape)x);

Or if you are doing this in .NET 2.0:

allShapes = cubes.ConvertAll<Shape>(delegate(Cube c){
    return (Shape)c;
});
Arnold Zokas
A: 

You can't cast between lists of types, even if the types themselves are convertible. You will need to create a new list and populate it, either by iterating the original list or by using ConvertAll. See http://www.dev102.com/2008/05/15/how-to-convert-listt1-to-listt2/ for sample code.

RichTea
+2  A: 

What you are referring to is called generic covariance and is not supported by c# 3. It is, however, supported by c# 4 (.NET 4 / VS 2010) and you can read more about it here:

Variance in Generic Interfaces

Having said that, IList<T> is not covariant (because it both accepts and exposes T). IEnumerable<T>, on the other hand, is covariant (because it doesn't accept T).

Richard Szalay
A: 

This works if you define allShapes as IEnumerable

In C# 4.0 you may simply assign allshapes=cubes

For C# 3.5 you could use allShapes = cubes.Select(c=>((Shape)c));

But in any case you need to use IEnumerable instead of List

cellik