Как преобразовать общий список‹T› в список на основе интерфейса‹T›

Я уверен, что мне не хватает чего-то простого, однако я пытаюсь преобразовать строго типизированный список объектов, которые реализуют интерфейс, в список этого типа интерфейса.

Ниже приведен пример, демонстрирующий ошибку:

public void ExampleCode(){
    List<Cube> cubes = new List<Cube>();
    List<Shape> allShapes;
    allShapes = cubes;//Syntax Error
    allShapes = (List<Shape>)cubes;//Syntax Error  
}

public class Cube : Shape
{
    public int ID { get; set; }
    public int Sides { get; set; }
}

public interface Shape
{
  int ID { get; set; }
  int Sides { get; set; }
}

person John    schedule 16.02.2010    source источник
comment
NB: код в вопросе сформулирован как приведение (т. е. другой вид одного и того же объекта). Различные обходные пути, приведенные ниже, копируют список - создается новый список, и каждый элемент Shape добавляется в новый список - предоставляя другой объект, а не другое представление одного и того же объекта.   -  person Binary Worrier    schedule 16.02.2010


Ответы (7)


Вместо такого кастинга попробуйте:

allShapes = cubes.Cast<Shape>().ToList();

Для этого вам понадобится .NET 3.5. Я считаю, что метод расширения Cast можно найти в System.Linq.

person Dave Markle    schedule 16.02.2010
comment
Обратите внимание, что это копирует список (хотя, если он содержит ссылки, будут скопированы ссылки, а не объекты). Это не то же самое, что приведение самого объекта списка. - person Richard; 16.02.2010
comment
Это максимально приближено к .NET 3.5. Невозможно привести объект списка, как было задано. - person Lasse V. Karlsen; 17.02.2010

Вы не можете. Потому что List<T> и ILIst<T> поддерживают только параметры инвариантного типа. Это связано с тем, что T используется как для входных, так и для выходных параметров (например, возвращаемых значений). В противном случае вы можете нарушить безопасность типов.

Другие интерфейсы (например, IEntumerable<T>) допускают некоторые различия.

См. блог Эрика Липперта "Fabulous Adventures In Coding" для обсуждения контра- и ко- дисперсия. В частности, тег "Covariance and Contravariance" .

Изменить, только что добавленное в блог «Часто задаваемые вопросы по C#»: Часто задаваемые вопросы о ковариантности и контравариантности

person Richard    schedule 16.02.2010

То, о чем вы говорите, называется универсальной ковариацией и не поддерживается С# 3. Однако оно поддерживается С# 4 (.NET 4/VS 2010), и вы можете узнать больше об этом здесь:

Разница в универсальных интерфейсах

Сказав это, IList<T> не является ковариантным (поскольку он и принимает, и выставляет T). IEnumerable<T>, с другой стороны, является ковариантным (поскольку не принимает T).

person Richard Szalay    schedule 16.02.2010

Вы не можете этого сделать, так как, применяя этот способ, вы потенциально можете потерять безопасность всех типов. Например, преобразование List<Shape> в List<object> приведет к тому, что в список могут быть добавлены объекты любого типа, что будет совершенно несовместимо.

person Anton Gogolev    schedule 16.02.2010

Вы также можете сделать:

allShapes = cubes.ConvertAll(x => (Shape)x);

Или, если вы делаете это в .NET 2.0:

allShapes = cubes.ConvertAll<Shape>(delegate(Cube c){
    return (Shape)c;
});
person Arnold Zokas    schedule 16.02.2010

Вы не можете выполнять приведение типов между списками, даже если сами типы являются конвертируемыми. Вам нужно будет создать новый список и заполнить его либо путем повторения исходного списка, либо с помощью ConvertAll. См. http://www.dev102.com/2008/05/15/how-to-convert-listt1-to-listt2/ для примера кода.

person Rich Tebb    schedule 16.02.2010

Это работает, если вы определяете allShapes как IEnumerable

В C# 4.0 вы можете просто назначить allshapes=cubes

Для С# 3.5 вы можете использовать allShapes = cubes.Select(c=>((Shape)c));

Но в любом случае вам нужно использовать IEnumerable вместо List

person cellik    schedule 16.02.2010