Generic Generics and Method Overloads
• CommentsI was happily coding along this week, adding more IList<T>
extension methods to my general utility library, when I came across an annoying problem. The following code works fine:
The behavior is just as you’d expect; the correct overloaded method is chosen based on the better conversion of the static types of the arguments.
So far, so good. The problem that I came across is when generic generics are used:
The compiler can choose the correct overload when the argument matches the specific expected type (e.g., list2
), but fails to deduce that one overload is better than another when the argument is not as specific (e.g., list3
).
The reasoning behind this is a bit obscure, but understandable. The compiler determines that it is able to convert the argument to either type:
However, when determining which overload is “better”, the compiler cannot convert from IList<IList<int>>
to IEnumerable<IEnumerable<int>>
, so it decides that neither overload is better, and therefore they are ambiguous. The first example worked because there is a conversion from IList<T>
to IEnumerable<T>
, so the IList<T>
overload was chosen.
Note also that this situation may change when .NET 4 comes out. .NET 4 introduces covariance and contravariance for generics. The concepts don’t apply to APIs that are both readable and writeable (e.g., IList<T>
), but they do apply to APIs that are one or the other (e.g., IEnumerable<T>
). It’s expected that .NET 4 will have an implicit conversion from IList<IList<int>>
to IEnumerable<IEnumerable<int>>
(because IList<IList<int>>
implements IEnumerable<IList<int>>
), but it’s unclear exactly how “smart” the compiler will be while resolving overload resolution.