ICollection <T>不协变?

Jef*_*nes 11 c# generics inheritance covariance

这样做的目的是同步包含图形边缘的两个集合,发送方和接收方,以便在发生某些事情时(删除边缘,添加边缘等)通知双方.

为此,对集合的(反向)引用包含在集合中的元素中

class EdgeBase {
    EdgeBase(ICollection<EdgeBase> rCol, ICollection<EdgeBase> sCol)     
    { RecvCol=rCol;  SendCol=sCol; }      
    ICollection<EdgeBase> RecvCol;      
    ICollection<EdgeBase> SendCol;       
    public virtual void Disconnect() // Synchronized deletion         
    { RecvCol.Remove(this);  SendCol.Remove(this); }                 
}         
class Edge : EdgeBase {       
    Edge(ICollection<EdgeBase> rCol, ICollection<EdgeBase> sCol)     
    : base(rCol, sCol) {}
    int Weight;     
}      
Run Code Online (Sandbox Code Playgroud)

删除(断开连接)没问题,但在创建过程中出现了问题:

HashSet<Edge> receiverSet, senderSet;
var edge = new Edge(receiverSet, senderSet); // Can't convert Edge to EdgeBase!
Run Code Online (Sandbox Code Playgroud)

虽然Edge是源于EdgeBase此,但这是非法的.(问题是Edge一部分,而不是HashSet<>一部分.)

在写完数百行之后,我发现ICollection<>它并不是协变的IEnumerable<>.

什么是解决方法?

编辑:

如果我在不破坏C#的协方差规则的情况下编写上面的代码,那就像这样:

public class EdgeBase<T, U>
    where T : ICollection<U<T>> // illegal
    where U : EdgeBase<T, U>    // legal, but introduces self-reference
{
    public EdgeBase(T recvCol, T sendCol) {...}
    protected T ReceiverCollection;
    protected T SenderCollection;
    public virtual void Disconnect() {...}
}
Run Code Online (Sandbox Code Playgroud)

但这是非法的; 'U'不能与形式参数T一起使用.

aia*_*tag 17

Eric Lippert说C#只支持类型安全的协方差和逆变.如果你想到它,制作ICollection协变不是类型安全的.

让我们说你有

ICollection<Dog> dogList = new List<Dog>();
ICollection<Mammal> mammalList = dogList; //illegal but for the sake of showing, do it
mammalList.Add(new Cat());
Run Code Online (Sandbox Code Playgroud)

你的mammalList(实际上是一个dogList)现在将包含一个Cat.

IEnumerable<T>是协变的,因为你不能Add这样......你只能从中读取 - 这反过来又保留了类型安全性.

  • 另一方面,`ICollection <Mammal>`可能包含一个`Cat`,这意味着它无法安全地转换为`ICollection <Dog>`.所以它也不能安全地做逆变. (7认同)