Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a covariance bug in C# 4?

In the following piece of code I expected to be able to implicitly cast from elements to baseElements because TBase is implicitly convertible to IBase.

public interface IBase { } public interface IDerived : IBase { } public class VarianceBug {     public void Foo<TBase>() where TBase : IBase     {         IEnumerable<TBase> elements = null;         IEnumerable<IDerived> derivedElements = null;         IEnumerable<IBase> baseElements;          // works fine         baseElements = derivedElements;          // error CS0266: Cannot implicitly convert type          //   'System.Collections.Generic.IEnumerable<TBase>' to          //   'System.Collections.Generic.IEnumerable<IBase>'.          //   An explicit conversion exists (are you missing a cast?)         baseElements = elements;     } } 

However, I get the error that is mentioned in the comment.

Quoting from the spec:

A type T<A1, …, An> is variance-convertible to a type T<B1, …, Bn> if T is either an interface or a delegate type declared with the variant type parameters T<X1, …, Xn>, and for each variant type parameter Xi one of the following holds:

  • Xi is covariant and an implicit reference or identity conversion exists from Ai to Bi

  • Xi is contravariant and an implicit reference or identity conversion exists from Bi to Ai

  • Xi is invariant and an identity conversion exists from Ai to Bi

Checking my code, it appears to be consistent with the spec:

  • IEnumerable<out T> is an interface type

  • IEnumerable<out T> is declared with variant type parameters

  • T is covariant

  • an implicit reference conversion exists from TBase to IBase

So - is it a bug in the C# 4 compiler?

like image 699
Omer Mor Avatar asked May 06 '10 17:05

Omer Mor


1 Answers

Variance only works for reference-types (or there is an identity conversion). It is not known that TBase is reference type, unless you add : class:

 public void Foo<TBase>() where TBase : class, IBase 

since I could write a:

public struct Evil : IBase {} 
like image 111
Marc Gravell Avatar answered Oct 09 '22 03:10

Marc Gravell