Это ковариационная ошибка в C # 4? - PullRequest
36 голосов
/ 06 мая 2010

В следующем фрагменте кода я ожидал, что смогу неявно приводить от 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 one имеет место следующее:

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

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

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

Проверяя мой код, кажется, что он соответствует спецификации:

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

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

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

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

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

Ответы [ 2 ]

50 голосов
/ 06 мая 2010

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

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

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

public struct Evil : IBase {}
14 голосов
/ 06 мая 2010

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

См. FAQ по ковариации и контравариантности:

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