Problem understanding covariance contravariance with generics in C#

  • A+

I can't understand why the following C# code doesn't compile.

As you can see, I have a static generic method Something with an IEnumerable<T> parameter (and T is constrained to be an IA interface), and this parameter can't be implicitly converted to IEnumerable<IA>.

Can someone explain it to me (I don't search for a workaround, just to understand why it doesn't work).

public interface IA { } public interface IB : IA { } public class CIA : IA { } public class CIAD : CIA { } public class CIB : IB { } public class CIBD : CIB { }  public static class Test {     public static IList<T> Something<T>(IEnumerable<T> foo) where T : IA     {         var bar = foo.ToList();          // All those calls are legal         Something2(new List<IA>());         Something2(new List<IB>());         Something2(new List<CIA>());         Something2(new List<CIAD>());         Something2(new List<CIB>());         Something2(new List<CIBD>());         Something2(bar.Cast<IA>());          // This call is illegal         Something2(bar);          return bar;     }      private static void Something2(IEnumerable<IA> foo)     {     } } 

Error I get in Something2(bar) line:

 Argument 1: cannot convert from 'System.Collections.Generic.List<T>'  to 'System.Collections.Generic.IEnumerable<ConsoleApp20.Program.IA>' 


The error message is insufficiently informative, and that is my fault. Sorry about that.

The problem you are experiencing is a consequence of the fact that covariance only works on reference types.

Now, you're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is IA. You said that T is a type which implements IA, and a value type can implement an interface.

If you want covariance to work you have to tell the compiler that the type parameter is a reference type with the class constraint as well as the IA interface constraint.

The error message really should say that the conversion is not possible because covariance requires a guarantee of reference-type-ness, since that is the fundamental problem.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: