Преобразовать List <T> в List <Interface>

111
public interface IDic
{
    int Id { get; set; }
    string Name { get; set; }
}
public class Client : IDic
{

}

Как я могу транслировать List<Client>на List<IDic>?

Андрей Калашников
источник

Ответы:

252

Вы не можете использовать его (сохраняя ссылочную идентичность) - это было бы небезопасно. Например:

public interface IFruit {}

public class Apple : IFruit {}
public class Banana : IFruit {}

...

List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());

// Eek - it's a banana!
Apple apple = apples[0];

Теперь вы можете конвертировать List<Apple>в IEnumerable<IFruit>в .NET 4 / C # 4 из - за ковариации, но если вы хотите List<IFruit>вы должны создать новый список. Например:

// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();

// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();

Но это не то же самое, что приведение исходного списка, потому что теперь есть два отдельных списка. Это безопасно, но вы должны понимать, что изменения, внесенные в один список , не будут видны в другом списке. ( Конечно, будут видны изменения в объектах , на которые ссылаются списки.)

Джон Скит
источник
6
как будто они «все еще в здании»!
Андрас Золтан
1
В чем разница между созданием списка ковариаций и выполнением new List <IFruit> (); затем выполнить foreach над исходным списком и добавить каждый элемент в список IFruit? По принципу foreach ... ссылка на объект будет такой же, верно? Итак ... если это правда, лично для меня не имеет особого смысла то, что вы не можете напрямую использовать весь список. ?
Роберт Ноак
3
@RobertNoack: Что вы имеете в виду под «ссылкой на объект»? Ссылка на объект каждого элемента одинакова, но это не то же самое, что приведение самой ссылки на список. Предположим, вы можете привести ссылку, чтобы у вас была ссылка типа времени компиляции, List<IFruit>которая на самом деле была ссылкой на файл List<Apple>. Что бы вы ожидали, если бы добавили Bananaссылку на это List<IFruit>?
Джон Скит
1
Ой. Думаю, мне нужно научиться читать. Я думал, что в исходном ответе говорилось, что объекты в списке будут глубоко скопированы и воссозданы, что не имело для меня смысла. Но я явно пропустил ту часть, где создается только новый список с теми же объектами.
Роберт Ноак
2
@ TrươngQuốcKhánh: Я понятия не имею , что любой из ваших код выглядит, или есть ли у вас usingдирективу System.Linq, или то , что вы пытаетесь вызвать его, я не знаю , как вы ожидаете , что я в состоянии помочь. Я предлагаю вам провести дополнительные исследования, и, если вы все еще застряли, задайте вопрос с минимально воспроизводимым примером .
Джон Скит
6

Итератор Cast и .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() сделает свое дело.

Первоначально я сказал, что ковариация будет работать, но, как правильно заметил Джон; нет, не будет!

И изначально я тоже тупо перестал ToList()звонить

Андраш Золтан
источник
Castвозвращает IEnumerable<T>, а не List<T>- и нет, ковариация не допускает этого преобразования, потому что это было бы небезопасно - см. мой ответ.
Джон Скит,
Со страницы, на которую вы ссылаетесь: «Только типы интерфейсов и типы делегатов могут иметь параметры вариантного типа»
Джон Скит,
@Jon - я понял, что ToList()отсутствует, прежде чем читать ваш комментарий; но да, как вы показали, конечно, Коварианс не сработает! Дох!
Андрас Золтан
Правильно. Ковариация все еще может помочь, поскольку это означает, что вам не нужен Castвызов в .NET 4, если вы укажете аргумент типа для ToList.
Джон Скит,
5

У меня тоже была эта проблема, и после прочтения ответа Джона Скита я изменил свой код с использования List<T>на использование IEnumerable<T>. Хотя это не отвечает на исходный вопрос OP о том, как я могу List<Client>выполнить приведениеList<IDic> , он избавляет от необходимости делать это и, таким образом, может быть полезен для других, кто сталкивается с этой проблемой. Это, конечно, предполагает, что код, требующий использования, List<IDic>находится под вашим контролем.

Например:

public void ProcessIDic(IEnumerable<IDic> sequence)
{
   // Implementation
}

Вместо того:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}
Iamond eezeƦ
источник
3

Если вы можете использовать LINQ, вы можете сделать это ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
Musefan
источник
3
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Марк Мессинг
источник
0

Это возможно только путем создания новых List<IDic>и переноса всех элементов.

Петр Аугускик
источник
Любой комментарий, почему был отклонен, поскольку общий смысл такой же, как и у всех других ответов?
Piotr Auguscik
Я предполагаю, что вас проголосовали против, потому что вы сказали, что можно создать только новый список, но другие написали об обратном ... хотя это не мой голос против)
Musefan
Что ж, они создают новые списки, но не с новым оператором, который не меняет того факта, что они делают.
Piotr Auguscik
0

В .Net 3.5 вы можете делать следующее:

List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());

Конструктор для List в этом случае принимает IEnumerable.
list, однако, можно преобразовать только в IEnumerable. Несмотря на то, что myObj может быть преобразован в ISomeInterface, тип IEnumerable не может быть преобразован в IEnumerable.

Кент Агилар
источник
Проблема в том, что это делает копию списка, большую часть времени вы хотите выполнять операции с исходным списком
ролики