29

Assume I have interface and class:

public interface ITree {}
public class Tree : ITree {}

As IEnumerable<T> is covariant, the code line below is compiled successfully:

IEnumerable<ITree> trees = new List<Tree>();

But when I put it into generic method:

public void Do<T>() where T : ITree
{
     IEnumerable<ITree> trees = new List<T>();
}

I get compiled error from compiler:

Error 1 Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?) D:\lab\Lab.General\Lab.General\Program.cs 83 40 Lab.General

Why covariance does not work in this case?

cuongle
  • 74,024
  • 28
  • 151
  • 206
  • 1
    possible duplicate of [Is this a covariance bug in C# 4?](http://stackoverflow.com/questions/2783233/is-this-a-covariance-bug-in-c-sharp-4) – nawfal Jul 07 '14 at 07:01

1 Answers1

34

That is because variance only works with reference types (classes, interfaces & delegates). Add a class constraint and it compiles just fine:

public static void Do<T>() where T : class, ITree
alexn
  • 57,867
  • 14
  • 111
  • 145
  • 1
    Thank, I mislead that when doing constraint `T : ITree` it is implicit that T is reference type, but it actually is not – cuongle Oct 05 '12 at 11:19
  • If `ITree` is an interface type, a storage location of type `ITree` will always hold either `null` or a reference to a heap object that implements `ITree`, but a storage location of a generic type constrained to `ITree` may hold a reference or it may hold an actual instance of a value type which implements `ITree`. Personally, I dislike the way that structs which implement interfaces are implicitly converted to heap references which implement those interfaces without the structs having any say in the matter. Value-type semantics differ from rerefence semantics in USEFUL ways, but... – supercat Oct 19 '12 at 22:53
  • ...the "unified type system" model assumes (wrongly) that they will behave identically. A variable of type `List.Enumerator` implements `IEnumerable`, but holds something whose behavior is very different from that of a variable of type `IEnumerable` which holds a reference to a `List.Enumerator`. – supercat Oct 19 '12 at 23:00
  • @supercat I guess you mean `IEnumerator`, not `IEnumerable`. It is true that value types get boxed when you store them in a variable of interface type. The **good** thing about generics is that we don't get boxing there. So if you say `Do()` where `MyStruct` is a struct implementing the interface, then you get a specific "version" of the method for `MyStruct`. So if inside the method it says `T localT = default(T);` then the code produces a value of `MyStruct` and makes **no** boxing which is cool. But with `ITree localT = default(T);` of course you get a boxed value. – Jeppe Stig Nielsen Nov 25 '12 at 13:55
  • @JeppeStigNielsen: You are correct. The problem is that a struct which implements a mutating interface will have struct semantics, but if cast to that interface it will become (and behave as) a reference type with a broken `Equals` method. While it's useful to have a concept of interfaces that can be implemented by value types, I'm not sure it's useful to say that a boxed structure implicitly implements the interfaces of the original; it might be more useful to allow structures to define casts to interfaces (where the cast would yield a new object which does implement the interface). – supercat Nov 25 '12 at 17:20
  • @JeppeStigNielsen: Actually, another thing that would have helped ease the confusion would have been to have the `For Each` and `foreach` in vb.net and C# check for the existence of a `GetDuckTypedEnumerator()` method before `GetEnumerator`, using the former if it exists. That would allow a class to have `GetEnumerator` return a *class-type* `IEnumerator` and have `GetDuckTypeEnumerator` return a struct which does not implement `IEnumerator`. If boxed types are going to have `Equals` report value equality (it can't report reference equality), they should be immutable. That would be... – supercat Nov 25 '12 at 17:24
  • @JeppeStigNielsen: ...a problem if one wanted to have a struct implement `IEnumerator`, but not a problem if one wanted to have a struct which was only used with duck-typed `ForEach`. – supercat Nov 25 '12 at 17:26
  • @supercat I studied `List<>.GetEnumerator()` a bit. It always returns a **mutable value-type**. If you call the "direct" instance method and assign to a `var` typed variable, you get no boxing, because the formal return type is the type of the struct (`List<>.Enumerator`). If you call the explicit interface implementations, of course your formal return type is a reference type (`IEnumerator` or `IEnumerator<>`), but you still gets the same struct value, only **boxed** of course. After that you have all the "unusual" features of mutable structs (calling `MoveNext()` mutates the value). – Jeppe Stig Nielsen Nov 26 '12 at 12:42