这样做的目的是同步两个集合,发送方和接收方,其中包含一个图边,以便在发生某些事情(删除边、添加边等)时通知双方。
为此,对集合的(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。你只能从中读出——这又保留了类型安全性。

08-05 04:47