C# Type Conversion Error Despite Generic Constraint
-
06-07-2019 - |
Question
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>
?
Solution
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 :)
OTHER TIPS
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
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.