ICollection not Covariant?

后端 未结 2 1055
予麋鹿
予麋鹿 2020-12-17 17:30

The purpose of this is to synchronize two collections, sender-side & receiver-side, containing a graph edge, so that when something happens (remove edge, add edge, etc)

相关标签:
2条回答
  • 2020-12-17 17:58

    You're messing with type safety basically. Your backing collection is an ICollection<EdgeBase> (which means you can add any EdgeBase into it) but what you're passing a very specific type, HashSet<Edge>. How would you add (or remove) AnotherEdgeBaseDerived into HashSet<Edge>? If that is the case then this should be possible:

    edge.Add(anotherEdgeBaseDerived); // which is weird, and rightly not compilable
    

    If you perform a cast yourself and pass a separate list then that's compilable. Something like:

    HashSet<Edge> receiverSet, senderSet;
    var edge = new Edge(receiverSet.Cast<EdgeBase>().ToList(), 
                        senderSet.Cast<EdgeBase>().ToList()); 
    

    which means your receiverSet and senderSet are now out of sync with base list in Edge. You can either have type safety or sync (same reference), you cant have both.

    I worry if there exist no good solution to this, but for a good reason. Either pass HashSet<EdgeBase> to Edge constructor (better) or let EdgeBase collections be ICollection<Edge> (which seems very odd to do).

    Or, the best you can have given the design constraints imo is generic

    class EdgeBase<T> where T : EdgeBase<T>
    {
    
    }
    
    class Edge : EdgeBase<Edge>
    {
        public Edge(ICollection<Edge> rCol, ICollection<Edge> sCol) : base(rCol, sCol)
        {
    
        }
    }
    

    Now you can call as usual:

    HashSet<Edge> receiverSet = new HashSet<Edge>(), senderSet = new HashSet<Edge>();
    var edge = new Edge(receiverSet, senderSet);
    

    To me the fundamental problem is the fuzzy and smelly design. An EdgeBase instance holding a lot of similar instances, including more derived ones? Why not EdgeBase, Edge and EdgeCollection separately? But you know your design better.

    0 讨论(0)
  • 2020-12-17 18:17

    Eric Lippert said that C# will only support type-safe covariance and contravariance. If you would think of it, making ICollection covariant is not type-safe.

    Let's say you have

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

    Your mammalList (which is actually a dogList) would now then contain a Cat.

    IEnumerable<T> is covariant because you cannot Add to it... you can only read from it -- which, in turn, preserves type-safety.

    0 讨论(0)
提交回复
热议问题