Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem understanding covariance contravariance with generics in C#

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>.

What is the explanation? (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' to 'System.Collections.Generic.IEnumerable'

like image 971
BenLaz Avatar asked Sep 26 '22 08:09

BenLaz


People also ask

What is covariance and Contravariance in generics?

Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types.

What is difference between covariance and Contravariance?

In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.

What is covariance and Contravariance in generics in Java?

Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them.

What is in and out in generics c#?

A type can be declared contravariant in a generic interface or delegate only if it defines the type of a method's parameters and not of a method's return type. In , ref , and out parameters must be invariant, meaning they are neither covariant nor contravariant.


1 Answers

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.

You're probably saying "but IA is a reference type" right now. Yes, it is. But you didn't say that T is equal to IA. You said that T is a type which implements IA, and a value type can implement an interface. Therefore we do not know whether covariance will work, and we disallow it.

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.

like image 219
Eric Lippert Avatar answered Oct 22 '22 08:10

Eric Lippert