В следующей части кода я ожидал мочь неявно бросить от 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?
Дисперсия работает только для ссылочных типов (или существует преобразование идентичности ). Неизвестно, что TBase
является ссылочным типом, если вы не добавите : class
:
public void Foo<TBase>() where TBase : class, IBase
, поскольку я мог бы написать:
public struct Evil : IBase {}
Марк прав - я как раз собирался вставить тот же ответ.
См. 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.