views:

725

answers:

4

when we can inherit from base class / interface, why can't we declare a List<> using same classes / interface

 interface A
    { }
    class B : A
    { }
    class C : B
    { }
    class Test
    {
        static void Main(string[] args)
        {
            A a = new C(); // OK
            List<A> listOfA = new List<C>(); // compiler Error
        }
    }

Is there a way around, Thanks

A: 

Because C# doesn't allow that type of inheritance conversion at the moment.

Noon Silk
First, this is a question of convertibility, not of inheritance. Second, covariance of generic types will not work on class types, only on interface and delegate types.
Eric Lippert
Well, I can hardly argue with you.
Noon Silk
+9  A: 

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();
Mark Byers
Thanks Mark, do appriciate.
Asad Butt
+5  A: 

First of all, stop using impossible-to-understand class names like A, B, C. Use Animal, Mammal, Giraffe, or Food, Fruit, Orange or something where the relationships are clear.

Your question then is "why can I not assign a list of giraffes to a variable of type list of animal, since I can assign a giraffe to a variable of type animal?"

The answer is: suppose you could. What could then go wrong?

Well, you can add a Tiger to a list of animals. Suppose we allow you to put a list of giraffes in a variable that holds a list of animals. Then you try to add a tiger to that list. What happens? Do you want the list of giraffes to contain a tiger? Do you want a crash? or do you want the compiler to protect you from the crash by making the assignment illegal in the first place?

We choose the latter.

This kind of conversion is called a "covariant" conversion. In C# 4 we will allow you to make covariant conversions on interfaces and delegates when the conversion is known to be always safe. See my blog articles on covariance and contravariance for details. (There will be a fresh one on this topic on both Monday and Thursday of this week.)

Eric Lippert
Thanks mate for correction and suggestions, will take care next time.
Asad Butt
+1  A: 

As far as why it doesn't work, it might be helpful to understand covariance and contravariance.

Just to show why this shouldn't work, here is a change to the code you provided:

void DoesThisWork()
{
     List<C> DerivedList = new List<C>();
     List<A> BaseList = DerivedList;
     BaseList.Add(new B());

     C FirstItem = DerivedList.First();
}

Should this work? The First item in the list is of Type "B", but the type of the DerivedList item is C.

Now, assume that we really just want to make a generic function that operates on a list of some type which implements A, but we don't care what type that is:

void ThisWorks<T>(List<T> GenericList) where T:A
{

}

void Test()
{
     ThisWorks(new List<B>());
     ThisWorks(new List<C>());
}
Chris