我对打字和反对/协约有疑问。
提供以下课程
public class BoardItemsHolderRepository<THolder, TBoardItem> : DataRepository<IList<THolder>>
where TBoardItem : BoardItem
where THolder : IBoardItemHolder<TBoardItem>
{
}
public interface IDataRepository<T> : IDataGetRepository<T>, IDataSetRepository<T> where T : class
{
}
public interface IDataGetRepository<out T> where T : class
{
IObservable<T> GetObservableStream();
IObservable<T> GetMostRecent();
}
public interface IDataSetRepository<in T> where T : class
{
void Set(T value);
}
public abstract class DataRepository<T> : IDataRepository<T> where T : class
{
..implementation details
}
public class ConstructHolder : IBoardItemHolder<Construct>
{
..implementation details
}
鉴于上述3个文件,有人可以向我解释为什么会发生以下情况吗?:
IDataGetRepository<IList<IBoardItemHolder<Construct>>> wontCompile = new BoardItemsHolderRepository<ConstructHolder, Construct>(); //illegal
IDataGetRepository<IList<ConstructHolder>> compile = new BoardItemsHolderRepository<ConstructHolder, Construct>(); //legal
我不明白为什么第一行的隐式强制转换不起作用,因为随后的行会编译(按预期)
IBoardItemHolder<Construct>> compile = new ConstructHolder();
答案 0 :(得分:3)
让我们逐步扩展非法行的右侧。首先,我们从
开始BoardItemsHolderRepository<ConstructHolder, Construct>
这是DataRepository<IList<THolder>>
,因此上面是一种:
DataRepository<IList<ConstructHolder>>
依次为IDataGetRepository<T>
,因此以上是一种:
IDataGetRepository<IList<ConstructHolder>>
IDataGetRepository<T>
在T
上是协变的。确切地回忆一下这是什么意思:如果 U
是T
的子类型,那么IDataGetRepository<U>
是IDataGetRepository<T>
的子类型。例如,IDataGetRepository<Cat>
是IDataGetRepository<Animal>
的子类型,因此可以将前一种类型的实例分配给后一种类型的变量。
但是,IDataGetRepository<IList<ConstructHolder>>
不是IDataGetRepository<IList<IBoardItemHolder<Construct>>>
的子类型,因此无法为其分配。为什么? 因为IList<ConstructHolder>
不是IList<IBoardItemHolder<Construct>>
的子类型! IList<T>
在T
上是不变的!
因此,根据类型检查器,您尝试做的事情违反了类型安全。也许尝试使用IEnumerable
而不是IList
?