3

Why, with a generic constraint on type parameter T of class P of "must inherit from A", does the first call succeed but the second call fail with the type conversion error detailed in the comment:

abstract class A { }

static class S
{
    public static void DoFirst(A argument) { }
    public static void DoSecond(ICollection<A> argument) { }
}

static class P<T>
    where T : A, new()
{
    static void Do()
    {
        S.DoFirst(new T());             // this call is OK

        S.DoSecond(new List<T>());      // this call won't compile with:

        /* cannot convert from 'System.Collections.Generic.List<T>'
           to 'System.Collections.Generic.ICollection<A>' */
    }
}

Shouldn't the generic constraint ensure that List<T> is indeed ICollection<A>?

acdx
  • 782
  • 1
  • 6
  • 13
  • 1
    Closing as dupe - please see http://stackoverflow.com/questions/981570/c-no-implict-conversion-from-classchild-to-classbase – Pavel Minaev Jul 31 '09 at 20:11

3 Answers3

7

This is an example of C#'s lack of covariance on generic types (C# does support array covariance). C# 4 will add this feature on interface types and also will update several BCL interface types to support it as well.

Please see C# 4.0: Covariance and Contravariance:

In this article I’ll try to cover one of the C# 4.0 innovations. One of the new features is covariance and contravariance on type parameters that is now supported by generic delegates and generic interfaces. First let’s see what does these words mean :)

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • 4
    This will not change in C# 4.0. `IList` and `ICollection` are _not covariant_. So even though C# 4.0 will add support for type covariance, this particular case will keep not working. – Pavel Minaev Jul 31 '09 at 20:09
  • Pavel, you seem to be confused here. The types in play are NOT IList and ICollection...they are List (the concrete type, not the interface) and ICollection. A List is ABSOLUTELY an ICollection, and that does make them covariant. You posted your comment in THREE posts, and apparently downvoted two of them...based on the wrong type...its not IList, its List! – jrista Aug 01 '09 at 17:52
0

The constraint has no effect on the problem; the issue is that you're passing a List in a parameter that requires ICollection--C# doesn't support covariance so you need to explicitly cast the list to an ICollection:

S.DoSecond((ICollection<A>) new List<T>());      // this call will be happy
STW
  • 44,917
  • 17
  • 105
  • 161
0

You have strongly typed the parameter for DoSecond as type ICollection<A>. Despite the fact that T is of type A, at compile time there is no implicit cast between List<T> and ICollection<A>. You will either need to create the list and cast it to ICollection<A> when you call DoSecond, or make DoSecond a generic method itself.

NOTE: This type of implicit cast should be supported in C# 4.0, which will provide much improved co/contravariance over what C# 3.0 offers.

jrista
  • 32,447
  • 15
  • 90
  • 130