Coder Perfect

Is there a simpler way to cast from a ListX> to a ListY>?

Problem

I know it’s possible to cast a list of items from one type to another one at a time as follows (assuming your object provides a public static explicit operator function to do the casting):

List<Y> ListOfY = new List<Y>();

foreach(X x in ListOfX)
    ListOfY.Add((Y)x);

Isn’t it, however, feasible to cast the full list at once? As an example,

ListOfY = (List<Y>)ListOfX;

Asked by Jimbo

Solution #1

If X can be transformed into Y, you should be able to use it.

List<Y> listOfY = listOfX.Cast<Y>().ToList();

Some things to keep in mind (thanks to the commenters!)

Answered by Jamiec

Solution #2

The variable for direct casting is (ListY>) = ListOfY ListOfX isn’t conceivable because it would necessitate ListT> type co/contravariance, which can’t be guaranteed in every situation. Please continue reading to learn about the remedies to this casting issue.

While it appears that being able to create code like this is normal,

List<Animal> animals = (List<Animal>) mammalList;

This is obviously a mistake, because we can guarantee that every mammal will be an animal:

List<Mammal> mammals = (List<Mammal>) animalList;

Because not all animals are mammals.

However, if you’re using C# 3 or higher, you can use

IEnumerable<Animal> animals = mammalList.Cast<Animal>();

This makes casting a little easier. This code is syntactically equal to your one-by-one adding code since it utilizes an explicit cast to convert each Mammal in the list to an Animal, and it will fail if the conversion fails.

If you want additional control over the casting / conversion process, you can use the ListT> class’s ConvertAll method, which can transform the items using a provided expression. It also has the advantage of returning a List rather than an IEnumerable, eliminating the need for.ToList().

List<object> o = new List<object>();
o.Add("one");
o.Add("two");
o.Add(3);

IEnumerable<string> s1 = o.Cast<string>(); //fails on the 3rd item
List<string> s2 = o.ConvertAll(x => x.ToString()); //succeeds

Answered by SWeko

Solution #3

To add to Sweko’s statement, I’d like to say:

Why did the cast come together?

var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y

is not conceivable because ListT> is invariant in Type T, thus whether X derives from Y doesn’t matter) – this is because ListT> is defined as:

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces

(Note that type T has no further variance modifiers in this declaration.)

If changeable collections aren’t needed in your design, an upcast on many of the immutable collections is conceivable, for example, assuming Giraffe is derived from Animal:

IEnumerable<Animal> animals = giraffes;

This is because IEnumerableT> enables covariance in T, which makes sense given that IEnumerable implies that the collection cannot be altered because it lacks methods for adding and removing items. In the declaration of IEnumerableT>, take note of the out keyword:

public interface IEnumerable<out T> : IEnumerable

(Here’s a more detailed explanation of why changeable collections, such as List, can’t support covariance while immutable iterators and collections can.)

Casting with .Cast()

As has been mentioned by others, CastT>() can be done to a collection to project a new collection of elements casted to T; but, if the cast on one or more members is not possible, an InvalidCastException will be thrown (which is the same behavior as doing the explicit cast in the OP’s foreach loop).

With OfTypeT>, you can filter and cast data ()

The potential InvalidCastException can be prevented by using.OfTypeT>() instead of.CastT>() if the input list contains components of distinct, incompatible types (). (.OfType>() determines whether an element may be converted to the target type before attempting the conversion and filters out kinds that are incompatible.)

foreach

Also, consider what would have happened if the OP had posted this instead: (note the explicit Y y in the foreach)

List<Y> ListOfY = new List<Y>();

foreach(Y y in ListOfX)
{
    ListOfY.Add(y);
}

that the casting will be attempted as well. InvalidCastException will be thrown if no cast is possible.

Examples

Given a straightforward (C#6) class structure, consider the following:

public abstract class Animal
{
    public string Name { get;  }
    protected Animal(string name) { Name = name; }
}

public class Elephant :  Animal
{
    public Elephant(string name) : base(name){}
}

public class Zebra : Animal
{
    public Zebra(string name)  : base(name) { }
}

When working with a collection of mixed kinds, keep the following in mind:

var mixedAnimals = new Animal[]
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach(Animal animal in mixedAnimals)
{
     // Fails for Zed - `InvalidCastException - cannot cast from Zebra to Elephant`
     castedAnimals.Add((Elephant)animal);
}

var castedAnimals = mixedAnimals.Cast<Elephant>()
    // Also fails for Zed with `InvalidCastException
    .ToList();

Whereas:

var castedAnimals = mixedAnimals.OfType<Elephant>()
    .ToList();
// Ellie

Only Elephants are removed from the equation; Zebras are not.

In response to Implicit Cast Operators

Without dynamic, user-defined conversion operators are only employed at compile-time*, so even if a conversion operator between Zebra and Elephant were added, the preceding run-time behavior of the conversion techniques would not change.

If we add a conversion operator to turn a Zebra into an Elephant, we get the following:

public class Zebra : Animal
{
    public Zebra(string name) : base(name) { }
    public static implicit operator Elephant(Zebra z)
    {
        return new Elephant(z.Name);
    }
}

Instead, the compiler will be able to modify the type of the below array from Animal[] to Elephant[], given that the Zebras may now be transformed to a homogenous collection of Elephants, using the aforementioned conversion operator:

var compilerInferredAnimals = new []
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

Using Run-Time Implicit Conversion Operators

*However, as Eric mentioned, the conversion operator can be obtained at runtime by using dynamic:

var mixedAnimals = new Animal[] // i.e. Polymorphic collection
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach (dynamic animal in mixedAnimals)
{
    castedAnimals.Add(animal);
}
// Returns Zed, Ellie

Answered by StuartLC

Solution #4

You can make use of ListY>. [Converter from Y to T]); ConvertAllT>([Converter from Y to T]);

Answered by Andrey

Solution #5

This isn’t entirely the answer to the question, but it might be useful for some: as @SWeko pointed out, ListX> can’t be cast into ListY> due to covariance and contravariance, but ListX> can be cast into IEnumerableY>, even with implicit cast.

Example:

List<Y> ListOfY = new List<Y>();
List<X> ListOfX = (List<X>)ListOfY; // Compile error

but

List<Y> ListOfY = new List<Y>();
IEnumerable<X> EnumerableOfX = ListOfY;  // No issue

The main benefit is that it avoids the creation of a new list in memory.

Answered by Xav987

Post is based on https://stackoverflow.com/questions/5115275/shorter-syntax-for-casting-from-a-listx-to-a-listy