Prześlij List <T> do List <Interface>

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

}

Jak mogę rzucić List<Client>się List<IDic>?

Andrzej Kałasznikow
źródło

Odpowiedzi:

250

Nie można rzucać go (zachowując tożsamość odniesienia) - to byłoby niebezpieczne. Na przykład:

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

Teraz możesz przekonwertować a List<Apple>na IEnumerable<IFruit>w .NET 4 / C # 4 ze względu na kowariancję, ale jeśli chcesz List<IFruit>, musisz utworzyć nową listę. Na przykład:

// 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();

Ale to nie to samo, co przesyłanie oryginalnej listy - ponieważ teraz są dwie oddzielne listy. Jest to bezpieczne, ale musisz zrozumieć, że zmiany wprowadzone na jednej liście nie będą widoczne na drugiej. ( Oczywiście modyfikacje obiektów , do których odnoszą się listy, będą widoczne).

Jon Skeet
źródło
6
jakby byli „nadal w budynku”!
Andras Zoltan
1
Jaka jest różnica między wykonywaniem listy kowariancji a tworzeniem nowej listy <IFruit> (); potem foreach nad oryginalną listą i dodając każdą pozycję do listy IFruit? W każdym przypadku ... odniesienie do obiektu byłoby takie samo, prawda? Więc ... jeśli to prawda, osobiście nie ma dla mnie sensu, że nie możesz bezpośrednio przesłać całej listy. ?
Robert Noack
3
@RobertNoack: Co rozumiesz przez „odniesienie do obiektu”? Odwołanie do obiektu każdego elementu jest takie samo, ale to nie to samo, co samo rzutowanie odwołania do listy. Załóżmy, że możesz rzutować odwołanie, tak że masz odwołanie typu czasu kompilacji, List<IFruit>które w rzeczywistości było odwołaniem do pliku List<Apple>. Czego byś się spodziewał, gdybyś dodał Bananado tego odniesienie List<IFruit>?
Jon Skeet,
1
O. Myślę, że muszę się nauczyć czytać. Myślałem, że oryginalna odpowiedź mówi, że obiekty na liście zostaną głęboko skopiowane i odtworzone, co nie ma dla mnie sensu. Ale wyraźnie przegapiłem część, w której tworzona jest tylko nowa lista z tymi samymi obiektami.
Robert Noack,
2
@ TrươngQuốcKhánh: Nie mam pojęcia , jak wygląda jakikolwiek twój kod, czy masz usingdyrektywę System.Linqlub do czego próbujesz ją wywołać, nie jestem pewien, jak oczekujesz, że będę w stanie pomóc. Proponuję przeprowadzić więcej badań, a jeśli nadal utkniesz, zadaj pytanie z minimalnym powtarzalnym przykładem .
Jon Skeet
6

Iterator Cast i .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() da rade.

Pierwotnie powiedziałem, że kowariancja zadziała - ale jak słusznie zauważył Jon; nie, nie będzie!

I pierwotnie też głupio przerwałem ToList()rozmowę

Andras Zoltan
źródło
Castzwraca IEnumerable<T>, a nie List<T>- i nie, kowariancja nie pozwoli na tę konwersję, ponieważ byłaby niebezpieczna - zobacz moją odpowiedź.
Jon Skeet
Ze strony, do której prowadziło łącze: „Tylko typy interfejsów i typy delegatów mogą mieć parametry typu wariantu”
Jon Skeet
@Jon - zdałem sobie sprawę, że ToList()brakuje go przed przeczytaniem twojego komentarza; ale tak, jak już pokazałeś, oczywiście kowariancja nie zadziała! No!
Andras Zoltan
Dobrze. Kowariancja może nadal pomóc, ponieważ oznacza, że ​​nie potrzebujesz Castwywołania w .NET 4, o ile określisz argument typu na ToList.
Jon Skeet,
5

Ja też miałem ten problem i po przeczytaniu odpowiedzi Jona Skeeta zmodyfikowałem kod z using List<T>do use IEnumerable<T>. Chociaż nie stanowi to odpowiedzi na pierwotne pytanie OP, jak mogę przesyłać List<Client>doList<IDic> , pozwala uniknąć takiej potrzeby, a zatem może być pomocne dla innych osób, które napotkają ten problem. To oczywiście zakłada, że ​​kod, który wymaga użycia, List<IDic>jest pod Twoją kontrolą.

Na przykład:

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

Zamiast:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}
Ɖiamond ǤeezeƦ
źródło
3

Jeśli możesz używać LINQ, możesz to zrobić ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
musefan
źródło
3
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Marc Messing
źródło
0

Jest to możliwe tylko poprzez tworzenie nowych List<IDic>i przenoszenie wszystkich elementów.

Piotr Auguscik
źródło
Jakiś komentarz, dlaczego odrzucono głos, skoro ogólne znaczenie jest takie samo jak wszystkie inne odpowiedzi?
Piotr Auguscik
Domyślam się, że zostałeś odrzucony, ponieważ powiedziałeś, że można utworzyć tylko nową listę, ale inni napisali coś przeciwnego ... nie mój przeciw)
musefan
Cóż, tworzą nowe listy, ale nie z nowym operatorem, co nie zmienia faktu, że to robią.
Piotr Auguscik
0

W .Net 3.5 możesz wykonać następujące czynności:

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

Konstruktor List w tym przypadku przyjmuje IEnumerable.
list, choć można ją zamienić tylko na IEnumerable. Mimo że myObj można przekonwertować na ISomeInterface, typ IEnumerable nie jest konwertowany na IEnumerable.

Kent Aguilar
źródło
Problem w tym, że tworzy kopię listy, przez większość czasu chcesz wykonywać operacje na oryginalnej liście
rolki