Как установить базовый элемент общего списка‹T›, используя производный конкретный список‹TUser›

У меня возникает ошибка компилятора при попытке установить общий член класса базовой коллекции в этом производном классе.

error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List<IntSegment>' to 'System.Collections.Generic.List<T>'

Вот схема моей общей коллекции

public class Path<T> : IEnumerable<T> where T : Segment
{
    private List<T> segments = new List<T>();

    public List<T> Segments
    {
        set { segments = value; }
        get { return this.segments; }
    }

    public Path()
    {
        this.Segments = new List<T>();
    }

    public Path(List<T> s)
    {
        this.Segments = s;
    }
}

Затем определяется производный универсальный класс этой коллекции для производного класса IntSegment of Segment (для которого определена базовая коллекция).

public class IntersectionClosedPath<T> : Path<T>, IEnumerable<T> where T : IntSegment
{
    public IntersectionClosedPath(List<IntSegment> inEdges)
        : base()
    {
        Segments = inEdges;
    }
}

Я не могу понять, почему это задание не разрешено. (Мне не нужно делать глубокую копию входящего списка).


person gwizardry    schedule 16.03.2012    source источник


Ответы (3)


Измените List<IntSegment> inEdges на List<T> inEdges и все заработает. Проблема в том, что Segments известен как List<T>, where T : IntSegment, а inEdges — как List<IntSegment>. (По причинам, которые я не буду вдаваться в подробности, если вы не спросите, такое назначение не разрешено. Посмотрите дисперсию/ковариантность/контравариантность, если вам интересно.)

person Tim S.    schedule 16.03.2012
comment
Спасибо, это работает ... я буду иметь в виду материал для чтения на потом. Я предполагаю, что единственный риск заключается в том, что у меня есть коллекция, которая должна содержать производные элементы, но не содержит - если я случайно отправлю список базы. Я не беспокоюсь об этом сейчас, так что нет проблем. - person gwizardry; 16.03.2012
comment
С существующими ограничениями дженериков вы не сможете скомпилировать что-то, что случайно отправит список базы. То, что вы отправляете, всегда должно быть List<T>, и T всегда должно точно совпадать. Например. это (предположим, что class ExtendedIntSegment : IntSegment существует) даже не скомпилируется: new IntersectionClosedPath<ExtendedIntSegment>(new List<IntSegment>()); Потому что ExtendedIntSegment и IntSegment не совсем одно и то же. Если вы сделаете эти два типа одинаковыми, любой из них будет компилироваться и работать нормально, с отличной безопасностью типов. - person Tim S.; 16.03.2012

Классическая проблема. List<Derived> нельзя неявно преобразовать в List<Base>.

Вы можете бросить элементы в List<Derived> и создать List<Base> следующим образом:

listOfBase = listOfDerived.Cast<Base>().ToList();
person Kendall Frey    schedule 16.03.2012
comment
Я педант, но это не совсем сработает - Cast<T>() возвращает IEnumerable<T>, а не List<T> - person Andras Zoltan; 16.03.2012
comment
Грр! Забыл ToList. Исправлено сейчас. - person Kendall Frey; 16.03.2012
comment
Я не совсем понимаю это, поскольку я не пытаюсь преобразовать базу в производную, а наоборот. - person gwizardry; 16.03.2012
comment
О! Должно быть, я еще сплю. - person Kendall Frey; 16.03.2012

List<T> не эквивалентен List<TBase>, где T : TBase.

Почему? Поскольку List<T> не наследуется от List<TBase>, связаны только параметры универсального типа.

Вместо этого вы можете сделать это в конструкторе:

Segments = inEdges.Cast<Segment>().ToList()

Я бы также изменил параметр конструктора на IEnumerable<IntSegment>

Точно так же может случиться так, что у @Tim S. есть лучшее решение, основанное на том, чего вы хотите достичь. Лично я считаю, что он, вероятно, прибил его.

person Andras Zoltan    schedule 16.03.2012
comment
Прав ли я, думая, что в любом случае, когда я получаю доступ к списку сегментов после приведения, у меня все еще будет доступ к членам IntSegment? - person gwizardry; 16.03.2012
comment
@gwizardry - В IntersectionClosedPath да. - person Andras Zoltan; 16.03.2012