views:

454

answers:

2

Say I have a method that is expecting a generic collection parameter of a base type, see Test.MethodA(IEnumerable(BaseClass) listA) below. How come when I pass it a collection of a derived type the code wont build? Wouldn't all instances of DerivedClass also be a BaseClass?

I could have just created a new List(BaseClass) and passed that to MethodA(IEnumerable(BaseClass) listA). But I would think C# would be smart enough to know that a collection of DerivedClass has all the same properties as a collection of BaseClass.

Is using the List.Cast(T)() method as I've shown the best way to solve this problem?

abstract class BaseClass
{
    public int SomeField;
    public abstract string SomeAbstractField
    {
        get;
    }
}

class DerivedClass:BaseClass
{
    public override string SomeAbstractField
    {
        get { return "foo"; }
    }
}

class TestClass
{ 

    public void MethodA(IEnumerable<BaseClass> listA)
    {

    }

    public void MethodB()
    {
        List<DerivedClass> listB = new List<DerivedClass>();

        //Error 16 The best overloaded method match for 
        //TestClass.MethodA(List<BaseClass>)' 
        //has some invalid arguments
        this.MethodA(listB);

        //this works
        this.MethodA(listB.Cast<BaseClass>());
    }
}
+7  A: 

Cast<>() is the best way to solve it at the moment. Your original version would work fine in C# 4.0 / .NET 4.0 though, where IEnumerable<T> is covariant in T.

(I've just verified it compiles under .NET 4.0 beta 1.)

Until .NET 4.0 and C# 4 come out, generics are invariant - IEnumerable<object> and IEnumerable<string> are effectively unrelated interfaces. Even in .NET 4.0, you wouldn't be able to do this with List<T> as the parameter type - only interfaces and delegates will be variant, and even then only when the type parameter is only used in appropriate positions (output positions for covariance, input positions for contravariance).

To learn more about variance in C# 4, read Eric Lippert's excellent series of blog posts about it.

Jon Skeet
+1 Yes this is a typical case of wait for .NET 4.0 :)
Zyphrax
@Downvoter: Why the downvote?
Jon Skeet
@Jon Skeet: you're the best. I knew the answer, but you've entered it faster. @Downvoter: why the downvote?
modosansreves
@Jon that was me. Apparently I can no longer be trusted to click on the correct arrow. I only noticed because I saw my rep go down and wondered who down voted me.
JaredPar
Downvoted by Microsoft... now I've really got to worry. I guess it's time to tear up my chapter on variance and start again ;)
Jon Skeet
@Jon, I would attribute the down vote to the performance bugs I'm working through today. They've made me click happy.
JaredPar
+1  A: 

In the general case (i.e. for collections that are modifiable), it is NOT the case that "a collection of DerivedClass has all the same properties as a collection of BaseClass": into a collection of Fruit you can insert Banana, Apple and Pear -- in a collection of Orange, you can't, so, you see, it doesn't have the same properties (under modification).

IEnumerable is a different case, since it's NOT modifiable, as as @Jon says this excessive restriction has been removed for that special case in 4.0.

Alex Martelli