72

How can i cast a List<object> to List<SomethingElse>?

(where SomethingElse is known to descend from object)


Bonus Chatter

Casting the list:

List<Object> first = ...;

List<SomethingElse> second = (List<SomethingElse>)first;

doesn't work:

Cannot convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List'

Casting the list:

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

doesn't work:

Cannot implicitely convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List'

i don't actually need the full List<T> object, just an ICollection<T> will do:

ICollection<SomethingElse> second = first;
ICollection<SomethingElse> second = (ICollection<SomethingElse>)first;
ICollection<SomethingElse> second = first.Cast<SomethingElse>();

don't work.

4

3 Answers 3

54

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.

1
  • i don't need a new list to be created immediately; but i do need a list that that can be modified, and isn't limited by the covariance problem. If ToList is what i need in order to get what i need, the i need to use it. i would, of course, prefer that the returned List was a wrapper around the first, allowing deferred execution as well.
    – Ian Boyd
    Jan 20, 2012 at 0:33
29

You have the option of using either Cast or OfType. Cast will throw an exception if you cannot cast to the type specified. OfType on the other hand will return only those items in the list that can be cast to the specified type. I would recommend using OfType in your situation.

List<Foo> fooList = myList.OfType<Foo>().ToList();
23

I think you're close with the Cast<T> expression. The difference is that Cast<T> returns an IEnumerable<T>, not a List<T>.

Try this:

IEnumerable<SomethingElse> second = first.Cast<SomethingElse>();

You can get a list by doing something similar:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.