How to Cast a List into a Type Which Inherits from List<T>

How can I cast a List into a type which inherits from List<T>?

You can make an extension method to do this job:

static class Extensions
{
public static Table ToTable<T>(this IEnumerable<T> collection) where T: Row
{
Table table = new Table();
table.AddRange(collection);
return table;
}
}

Now you can simply call this method:

table = tableObject.Where(x => x.Value.Equals("")).ToTable();

Or you can do it directly, since you create an empty Table:

Table table = new Table();
table.AddRange(tableObject.Where(x => x.Value.Equals("")));
return table;

Converting a List of Base type to a List of Inherited Type

If you have linq available you can do

var ListOfA = ListOfB.Cast<A>().ToList();

Cast List<T> to List<Interface>

You can't cast it (preserving reference identity) - that would be unsafe. For example:

public interface IFruit {}

public class Apple : IFruit {}
public class Banana : IFruit {}

...

List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());

// Eek - it's a banana!
Apple apple = apples[0];

Now you can convert a List<Apple> to an IEnumerable<IFruit> in .NET 4 / C# 4 due to covariance, but if you want a List<IFruit> you'd have to create a new list. For example:

// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();

// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();

But this is not the same as casting the original list - because now there are two separate lists. This is safe, but you need to understand that changes made to one list won't be seen in the other list. (Modifications to the objects that the lists refer to will be seen, of course.)

Convert List<DerivedClass> to List<BaseClass>

The way to make this work is to iterate over the list and cast the elements. This can be done using ConvertAll:

List<A> listOfA = new List<C>().ConvertAll(x => (A)x);

You could also use Linq:

List<A> listOfA = new List<C>().Cast<A>().ToList();

How to cast List<object> to List<SomethingElse>

LINQ, as implemented through the extension methods within the Enumerable class, relies on deferred execution:

Methods that are used in a query that returns a sequence of values do not consume the target data until the query object is enumerated. This is known as deferred execution.

Cast<T> does not create a new list immediately, but rather stores all the information that is required to perform the action. The list would only get enumerated when required (for example, through a foreach statement).

In your case, if you simply intend to iterate over the sequence, you should consider sticking to the IEnumerable<T> interface, which is the declared return type of Cast<T>:

IEnumerable<SomethingElse> second = first.Cast<SomethingElse>();
foreach (SomethingElse se in second)
{
// ...
}

This is efficient, since it only performs the cast as each item is iterated.

If you’re convinced you want a new list to be created immediately, use ToList:

List<SomethingElse> second = first.Cast<SomethingElse>().ToList();

Edit: Replying to point posted in comment:

It depends on what you mean by “a list that can be modified”. There are several LINQ query operators that will allow you to alter the definition of your query further. For example, if you want to remove all SomethingElse elements whose IsDeleted property is true, you can use the Where operator:

IEnumerable<SomethingElse> second = first.Cast<SomethingElse>();
second = second.Where(element => !element.IsDeleted);

If you want to add a sequence of new elements, you can use the Concat operator:

second = second.Concat(anotherCollectionOfSomethingElse);

If you want to sort your sequence in ascending order of ID, use the OrderBy operator:

second = second.OrderBy(element => element.ID);

Each time, we’re applying a query operator over the former definition of our query, and assigning the new (composite) query to our second variable. LINQ would store all your operators in the query definition. Then, when the sequence is actually enumerated (for example, through a foreach or ToList), it would give you the composite result of your sequence, with all the query operators applied in order.

As with all cases of deferred execution / lazy evaluation, be careful not to go overboard with this. If, for example, you’re going to apply a Where operator which will reduce the size of your sequence drastically, it might make sense to execute the query eagerly and store the enumerated list instead.

Type inherited from List<T> cannot be assigned with List<T>

How can I fix this?

Change your design. You cannot downcast - a MyList is a List<string> but not vice-versa. You can copy the items into the new list by re-using the List<T> copy constructor that accepts any IEnumerable<T> (which includes a List<string>):

public class ListInheritanceTest
{
public void Test1()
{
List<string> list = new List<string>();
MyList myList = new MyList(list); // copy the items in the list
}
}
public class MyList : List<string>
{
public MyList(IEnumerable<string> list) : base(list) {} // reuse the List<T> copy constructor
}

The purpose is to convert all List to MyList in the code without introducing any backward incompatibility.

Again, you can't. A List is not a MyList. The best you can do is convert the List to a MyList by using a copy constructor or some other method.

What's the easiest way to assign a List<string> to MyList.

You can't. A List<string> is not necessarily a MyList. In the same way that a Cat is not a Tiger. It MAY be, but you can't just cast is and treat it as if it were.

Note that if you created the List as a MyList (e.g. List<T> newList = new MyList()) then you can safely cast it. In fact you could then use it everywhere else as if it was a List<string> (because it is!) But you can't safely cast an arbitrary List<string> to a MyList. Period.

Why can't I cast from a List<MyClass> to List<object>?

The reason this is not legal is because it is not safe. Suppose it were legal:

List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = giraffes; // this is not legal; suppose it were.
animals.Add(new Tiger()); // it is always legal to put a tiger in a list of animals

But "animals" is actually a list of giraffes; you can't put a tiger in a list of giraffes.

In C# this is, unfortunately, legal with arrays of reference type:

Giraffe[] giraffes = new Giraffe[10];
Animal[] animals = giraffes; // legal! But dangerous because...
animals[0] = new Tiger(); // ...this fails at runtime!

In C# 4 this is legal on IEnumerable but not IList:

List<Giraffe> giraffes = new List<Giraffe>();
IEnumerable<Animal> animals = giraffes; // Legal in C# 4
foreach(Animal animal in animals) { } // Every giraffe is an animal, so this is safe

It is safe because IEnumerable<T> does not expose any method that takes in a T.

To solve your problem you can:

  • Create a new list of objects out of the old list.
  • Make the method take an object[] rather than a List<object>, and use unsafe array covariance.
  • Make the method generic, so it takes a List<T>
  • Make the method take IEnumerable
  • Make the method take IEnumerable<object> and use C# 4.


Related Topics



Leave a reply



Submit