ICollection <T> не ковариант?

Целью этого является синхронизация двух коллекций, стороне отправителя и получателя, содержащей край графика, так что когда что-то происходит (удаляем край, добавляем кромку и т.д.), обе стороны уведомляются.

Чтобы сделать это, обратные ссылки на коллекции были включены в элемент в коллекциях

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;     
}      

Deletion (Disconnect) было нормально, но проблема возникла во время создания:

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

Хотя Edge получен из EdgeBase, это незаконно. (Проблема состоит в Edge part, not HashSet<> part.)

После написания сотен строк, которые я обнаружил, ICollection<> не является ковариантным, как IEnumerable<>.

Что может быть обходным способом?

EDIT:

Если бы я написал код выше, не нарушая правила ковариации С#, это было бы так:

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.

Ответ 1

Эрик Липперт сказал, что С# будет поддерживать только ковариацию и контравариантность по типу. Если вы думаете об этом, создание ICollection covariant не безопасно для типов.

Скажем, у вас есть

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 к нему... вы можете читать только его, что, в свою очередь, сохраняет безопасность типов.

Ответ 2

В основном вы возитесь с безопасностью типа. Ваша резервная коллекция - ICollection<EdgeBase> (это означает, что вы можете добавить в нее все EdgeBase), но то, что вы передаете очень конкретный тип, HashSet<Edge>. Как бы вы добавили (или удалили) AnotherEdgeBaseDerived в HashSet<Edge>? Если это так, то это должно быть возможно:

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

Если вы выполняете бросок самостоятельно и передаете отдельный список, то этот компилируемый. Что-то вроде:

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

что означает, что ваши receiverSet и senderSet теперь не синхронизированы с базовым списком в Edge. Вы можете либо иметь безопасность по типу, либо синхронизацию (та же ссылка), , у вас не могут быть оба.

Я беспокоюсь, если нет хорошего решения, но по уважительной причине. Либо перейдите HashSet<EdgeBase> в Edge конструктор (лучше), либо пусть EdgeBase коллекции будут ICollection<Edge> (что кажется очень странным).

Или, самое лучшее, что вы могли бы дать проектным ограничениям imo, является общим

class EdgeBase<T> where T : EdgeBase<T>
{

}

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

    }
}

Теперь вы можете позвонить как обычно:

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

Для меня основной проблемой является нечеткий и вонючий дизайн. Экземпляр EdgeBase, содержащий много похожих экземпляров, включая более производные? Почему бы не EdgeBase, Edge и EdgeCollection отдельно? Но вы знаете, что ваш дизайн лучше.