Это ошибка ковариации в С# 4?

В следующем фрагменте кода я ожидал, что он сможет неявно отбрасывать от elements до baseElements, потому что TBase неявно конвертируется в 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;
    }
}

Однако я получаю сообщение об ошибке, указанное в комментарии.

Цитата из спецификации:

Тип T<A1, …, An> является дисперсионно-конвертируемым для типа T<B1, …, Bn>, если T является либо интерфейсом, либо типом делегата, объявленным с параметрами типа варианта T<X1, …, Xn>, и для каждого параметра типа варианта Xi один имеет место следующее:

  • Xi является ковариантным и существует неявное обращение ссылки или идентичности от Ai до Bi

  • Xi является контравариантным и существует неявное обращение ссылки или идентичности от Bi до Ai

  • Xi является инвариантным и существует тождественное преобразование от Ai до Bi

Проверка моего кода, похоже, соответствует спецификации:

  • IEnumerable<out T> - тип интерфейса

  • IEnumerable<out T> объявляется с параметрами типа варианта

  • T является ковариантным

  • существует неявное ссылочное преобразование от TBase до IBase

Итак - это ошибка в компиляторе С# 4?

Ответ 1

Отклонение работает только для ссылочных типов (или есть преобразование идентичности). Неизвестно, что TBase является ссылочным типом, если вы не добавите : class:

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

так как я мог бы написать:

public struct Evil : IBase {}

Ответ 2

Марк прав - я как раз собирался вставить тот же ответ.

См. часто задаваемые вопросы о ковариации и контравариантности:

http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

Из FAQ:

"Разница поддерживается только в том случае, если параметр типа является ссылочным типом.

Разница не поддерживается для типов значений

Следующее не компилируется:

// int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.