Действительно ли это - ошибка ковариантности в C# 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

Таким образом - действительно ли это - ошибка в компиляторе C# 4?

37
задан Alexander R 10 August 2012 в 12:54
поделиться

2 ответа

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

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

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

public struct Evil : IBase {}
51
ответ дан 27 November 2019 в 04:42
поделиться

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

См. 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.
14
ответ дан 27 November 2019 в 04:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: