Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using generic contravariant with IList and IEnumerable

Tags:

c#

.net

generics

I'm learning C# generics and making some dummy code for testing purposes. So, I'm testing the in Generic Modifier, which specifies that the type parameter is contravariant.

Given the below interface:

public interface IInterfaceTest<in T>
{
    void Method(T value);
    void Method(IList<T> values);
    void Method(IEnumerable<T> values);
}

When compiling, I'm getting the error message:

[CS1961] Invalid variance: The type parameter 'T' must be invariantly valid on 'IInterfaceTest.Method(IList)'. 'T' is contravariant.

The error is related only with the line void Method(IEnumerable<T> values). If this line is removed, all works fine.

So my question is: Why can I use the generic contravariant with IEnumerable but does not with IList? Am I forgot something?

Thanks.

like image 611
Bruno Peres Avatar asked Sep 28 '17 16:09

Bruno Peres


1 Answers

The question why it's not allowed for IList<T> has been answered in the comments and linked questions already: IList<T> is invariant in T and so a contra-variant T cannot be used here whatsoever.

What puzzled me at first is the fact that Method(IEnumerable<T>) is allowed here. The strange thing is that variance is "turned around" when you use the T as a type argument for another generic type.

Imagine this.

public interface ITestInterface<in T>
{
    void Method(IEnumerable<T> e);
    IEnumerable<T> GetMethod(); // illegal
}
public class Animal {}
public class Lion : Animal [}
public class Gnu : Animal {}

ITestInterface<Animal> animals;
ITestInterface<Lion> lions;
ITestInterface<Gnu> gnus;

Now the contra-variance of ITestInterface<in T> in T tells us that you can do

lions = animals;

And when you call lions.Method(e), you can only provide an IEnumerable<Lion>. So the code of Method can only enumerate Lions, which are all Animals as animals.Method() expects. Everything is fine.

On the other hand, the IEnumerable<T> GetMethod() is illegal, because:

gnus = animals;

is legal, and now gnu.GetMethod() would return an IEnumerable<Animal> where you'd expect an IEnumerable<Gnu>. And when you iterated, suprising animals could wait in that sequence.

like image 64
René Vogt Avatar answered Oct 23 '22 03:10

René Vogt