这样做的目的是同步两个集合,发送方和接收方,其中包含一个图边,以便在发生某些事情(删除边、添加边等)时通知双方。
为此,对集合的(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());
您的
mammalList
(实际上是dogList
)现在将包含Cat
。IEnumerable<T>
是协变的,因为您无法对其进行Add
。你只能从中读出——这又保留了类型安全性。