0

In the below example, I can add have a List of type Animal. Since Dog and Cat derive from animal, the List can hold each. However, if I have a method that accepts List<Animal>, you can not pass in a reference List<Dog>. If Dog is derived from animal, why is this not valid? However if i have a method that excepts a parameter of type Object, and all objects derive from Object, it works.

class Program
{
    static void Main(string[] args)
    {
       List<Animal> animals = new List<Animal>();
       animals.Add(new Dog() { Color = "Black" }); // allowed since Dog derives from Animal

       List<Dog> dogs = new List<Dog>();
       dogs.Add(new Dog() { Color = "White" });

       Test(animals);
       Test(dogs); // not allowed - does not compile
       Test2(dogs); // valid as all objects derive from object
    }

    static void Test(List<Animal> animals) {
        // do something
    }

    static void Test2(object obj) { 

    }
}

public class Animal {

   public String Color { get; set; }

}

public class Dog : Animal { }
public class Cat : Animal { }
PhillyNJ
  • 3,859
  • 4
  • 38
  • 64

2 Answers2

2

Call Test(dogs.Cast<Animal>().ToList()). Change parameter type to IEnumerable to skip ToList() which creates a copy.

int is an object. List<Animal> is an object, as is List<Dog>. But List<Dog> is NOT List<Animal>.

Gerino
  • 1,943
  • 1
  • 16
  • 21
2

Imagine that Test(dogs) did compile. Then imagine that Test() is implemented like follows:

static void Test(List<Animal> animals)
{
    animals.Add(new Cat()); // Where Cat is derived from Animal
}

You've just added a cat to a list of dogs... Clearly that can't be allowed. And that's why it's not.

However, if you are only accessing the elements of the list rather than adding to the list, you can declare Test() like so:

static void Test(IEnumerable<Animal> animals)
{
    foreach (var animal in animals)
    {
        // Do something with animal.
    }
}

Then you can pass any collection type which implements IEnumerable<T>, such as List<T> and plain arrays.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • That makes a lot sense now. – PhillyNJ Feb 24 '15 at 15:24
  • Well, adding a cat to dogs usually is hilarious, so I'd allow it... ;) – Gerino Feb 24 '15 at 15:24
  • Dogs and Cats... in the same `List` ... [Mass Hysteria!!!](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0CCUQtwIwAQ&url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D9S4cldkdCjE&ei=vZfsVI6mL-bHsQSBuYDgCw&usg=AFQjCNGzPe3S7AsY6QTfnKAu-096C6WMcQ&sig2=9ik5utUb2D5HjVV7AOZ8jw&bvm=bv.86475890,d.cGU) – D Stanley Feb 24 '15 at 15:25
  • 1
    Depending on the usage: IReadOnlyCollection is covariant. You should be able to pass that as a parameter. It -knows- you're not going to add items. – Caramiriel Feb 24 '15 at 15:25
  • @Caramiriel True, but in that case it might be better to use `IEnumerable` which works with more collection types. (Note that if you're using .Net 4 or earlier, you can't use `IReadOnlyCollection`.) – Matthew Watson Feb 24 '15 at 15:30