71

I want to do something like this:

List<Child> childList = new List<Child>();
...
List<Parent> parentList = childList;

However, because parentList is a List of Child's ancestor, rather than a direct ancestor, I am unable to do this. Is there a workaround (other than adding each element individually)?

Matthew
  • 28,056
  • 26
  • 104
  • 170
  • 3
    It's not an exact duplicate, but there's a very similar question here: http://stackoverflow.com/questions/1569774/ienumerablet-conversion – bcat Nov 22 '09 at 04:29

6 Answers6

98

Using LINQ:

List<Parent> parentList = childList.Cast<Parent>().ToList();

Documentation for Cast<>()

recursive
  • 83,943
  • 34
  • 151
  • 241
  • 3
    Note that this creates a copy of `childList`, exactly like the not-using-LINQ version by @Andre Pena. – dtb Nov 22 '09 at 04:28
  • 3
    List parentList = childList.OfType().ToList(); also works, and is preferable in cases where you are less sure of the content of the starting list. – Cylon Cat Nov 22 '09 at 04:28
  • 1
    @dtb, you get a new list, but I suspect there's a good chance that the objects in the list are the same objects. – Cylon Cat Nov 22 '09 at 04:30
  • 4
    If Child inherits from Parent, then you can be 100% certain that the cast will work anyway. – recursive Nov 22 '09 at 04:32
  • 1
    Besides, C# 4 will bring in more convenient conversion, http://msdn.microsoft.com/en-us/library/ee207183(VS.100).aspx – Lex Li Nov 22 '09 at 09:14
  • 2
    lextm: not for lists. `List<>`s are neither covariant nor contravariant. – recursive Nov 23 '09 at 22:34
  • Maybe I am missing something, but this exactly what the post author tried to do, which does NOT work. At run time you will get a System.InvalidCastException, because the type safe collection does not allow the cast. (Using Linq doesn't change a thing). See what Eric Lippert wrote. – RWC Feb 05 '14 at 13:35
  • 1
    The thing you are missing is that this construction creates a brand new list, which will not throw any exceptions at run time. – recursive Feb 05 '14 at 17:23
  • 3
    @recursive true, so you can use the new list of Parent objects to iterate over,etc. But the List is a new object and not a reference to the original, so if you remove one of its elements, the original List will not be modified, for example. – Boluc Papuccuoglu Oct 18 '14 at 09:47
66

Casting directly is not allowed because there's no way to make it typesafe. If you have a list of giraffes, and you cast it to a list of animals, you could then put a tiger into a list of giraffes! The compiler wouldn't stop you, because of course a tiger may go into a list of animals. The only place the compiler can stop you is at the unsafe conversion.

In C# 4 we'll be supporting covariance and contravariance of SAFE interfaces and delegate types that are parameterized with reference types. See here for details:

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

ryanwebjackson
  • 1,017
  • 6
  • 22
  • 36
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 137
    Even worse, if the compiler let you do that, your list would end up smaller than expected because the tiger would eat a few of the giraffes. – JoeCool Feb 03 '11 at 22:11
  • Does anything change if the type parameter for the "parent" is an interface? It appears that a covariant collection interface solves OP's original problem. – ryanwebjackson Jun 15 '20 at 22:09
13

Back in 2009 Eric teased us that things would change in C# 4. So where do we stand today?

The classes used in my answer can be found at the bottom. To make this easier to follow, we will use a Mammal class as "parent", and Cat and Dog classes as "children". Cats and dogs are both mammals, but a cat is not a dog and a dog is not a cat.

This still isn't legal, and can't be:

List<Cat> cats = new List<Cat>();

List<Mammal> mammals = cats;

Why not? Cats are mammals, so why can't we assign a list of cats to a List<Mammal>?

Because, if we were allowed to store a reference to a List<Cat> in a List<Mammal> variable we would then be able to compile the following code to add a dog to a list of cats:

mammals.Add(new Dog());

We mustn't allow that! Remember, mammals is just a reference to cats. Dog does not descend from Cat and has no business being in a list of Cat objects.

Starting with .NET Framework 4, several generic interfaces have covariant type parameters declared with the out Generic Modifier keyword introduced in C# 4. Amongst these interfaces is IEnumerable<T> which of course is implemented by List<T>.

That means we can now cast a List<Cat> to an IEnumerable<Mammal>:

IEnumerable<Mammal> mammalsEnumerable = cats;

We can't add a new Dog to mammalsEnumerable because IEnumerable<out T> is a "read-only" interface i.e. it has no Add() method, but we can now use cats wherever a IEnumerable<Mammal> can be consumed. For example, we can concatenate mammalsEnumerable with a List<Dog> to return a new sequence:

void Main()
{
    List<Cat> cats = new List<Cat> { new Cat() };
    IEnumerable<Mammal> mammalsEnumerable =
        AddDogs(cats); // AddDogs() takes an IEnumerable<Mammal>
    Console.WriteLine(mammalsEnumerable.Count()); // Output: 3. One cat, two dogs.
}

public IEnumerable<Mammal> AddDogs(IEnumerable<Mammal> parentSequence)
{
    List<Dog> dogs = new List<Dog> { new Dog(), new Dog() };
    return parentSequence.Concat(dogs);
}

Class definitions:

public abstract class Mammal { }

public class Cat: Mammal { }

public class Dog : Mammal { }
Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
-1

Don't do this:

List<Parent> parentList = childList.Cast<Parent>().ToList();

A child class should not be used as parent, if you want to pass a list as parameter of a method that receives a list<parent>, you need to change the method parameter using IEnumerable, more info IEnumerable.

public void methodA(IEnumerable<parent> param){}

Now you can use this method like this:

var oListChild = new List<child>();
methodA(oListChild);
  • List already is (implements) IEnumerable (which in term implements IEnumerable). The documentation link you included is useful, but this answer doesn't explain how one would create (or cast) an IEnumerable to IEnumerable. – ryanwebjackson Oct 31 '22 at 18:31
-2

yes, you can do it like

var result = List.And(x => x.Parent.All(b => b.ParentId == value));
Filip Cornelissen
  • 3,682
  • 3
  • 31
  • 41
Caner
  • 813
  • 1
  • 12
  • 26
-3

You can do this by using a Linq approach of the apply extension method, i.e.:

List<Parent> parentList = childList.Cast<Parent>().ToList();
Krisztián Balla
  • 19,223
  • 13
  • 68
  • 84
Md Shoaib Alam
  • 340
  • 1
  • 3
  • 13