这样做的目的是同步两个集合,发送方和接收方,包含一个图形边缘,这样当发生某些事情(删除边缘,添加边缘等)时,通知双方.
为了做到这一点,对集合的(back-)引用被包含在集合中的元素中
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; }
删除(断开连接)是确定,但问题发生在创建期间:
HashSet<Edge> receiverSet,senderSet; var edge = new Edge(receiverSet,senderSet); // Can't convert Edge to EdgeBase!
虽然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() {...} }
但这是非法的’U’不能与形式参数T一起使用.
解决方法
Eric Lippert said that C# will only support type-safe covariance and contravariance.如果你想到它,使ICollection协变不是类型安全的.
让我们说你有
ICollection<Dog> dogList = new List<Dog>(); ICollection<Mammal> mammalList = dogList; //illegal but for the sake of showing,do it mammalList.Add(new Cat());
你的哺乳动物列表(实际上是一个dogList)现在将包含一只猫.
IEnumerable是协变的,因为你不能添加到它…你只能从它读取 – 反过来保留类型安全.