Cast List <T> ke List <Interface>

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

}

Bagaimana cara mentransmisikan List<Client>ke List<IDic>?

Andrew Kalashnikov
sumber

Jawaban:

250

Anda tidak dapat mentransmisikannya (mempertahankan identitas referensi) - itu tidak aman. Sebagai contoh:

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

Sekarang Anda dapat mengonversi a List<Apple>ke IEnumerable<IFruit>dalam .NET 4 / C # 4 karena kovarian, tetapi jika Anda menginginkannya, List<IFruit>Anda harus membuat daftar baru . Sebagai contoh:

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

Tetapi ini tidak sama dengan mentransmisikan daftar aslinya - karena sekarang ada dua daftar terpisah . Ini aman, tetapi Anda perlu memahami bahwa perubahan yang dilakukan pada satu daftar tidak akan terlihat di daftar lainnya. (Modifikasi pada objek yang dirujuk daftar tersebut akan terlihat, tentu saja.)

Jon Skeet
sumber
6
seperti mereka 'masih di dalam gedung'!
Andras Zoltan
1
Apa perbedaan antara melakukan daftar kovariansi dan melakukan Daftar baru <IFruit> (); lalu melanjutkan daftar asli dan menambahkan setiap item ke daftar IFruit? Di setiap cara ... referensi objek akan sama, benar? Jadi ... jika itu benar, tidak masuk akal bagi saya secara pribadi bahwa Anda tidak bisa langsung membuang seluruh daftar. ?
Robert Noack
3
@RobertNoack: Apa yang Anda maksud dengan "referensi objek"? Referensi objek setiap elemen adalah sama, tetapi itu tidak sama dengan mentransmisikan referensi daftar itu sendiri. Misalkan Anda dapat mentransmisikan referensi, sehingga Anda memiliki referensi jenis waktu kompilasi List<IFruit>yang sebenarnya merupakan referensi ke a List<Apple>. Apa yang Anda harapkan terjadi jika Anda menambahkan Bananareferensi untuk itu List<IFruit>?
Jon Skeet
1
Oh. Saya rasa saya perlu belajar membaca. Saya pikir jawaban asli mengatakan bahwa objek dalam daftar akan disalin mendalam dan dibuat ulang yang tidak masuk akal bagi saya. Tapi saya jelas melewatkan bagian di mana hanya daftar baru yang dibuat dengan objek yang sama.
Robert Noack
2
@ TrươngQuốcKhánh: Saya tidak tahu seperti apa kode Anda, atau apakah Anda memiliki usingarahannya System.Linq, atau untuk apa Anda mencoba menyebutnya, saya tidak yakin bagaimana Anda mengharapkan saya dapat membantu. Saya sarankan Anda melakukan lebih banyak riset, dan jika Anda masih buntu, Anda mengajukan pertanyaan dengan contoh minimal yang dapat direproduksi .
Jon Skeet
6

A Cast iterator dan .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() akan melakukan triknya.

Awalnya saya mengatakan bahwa kovarian akan berhasil - tetapi seperti yang ditunjukkan Jon dengan benar; tidak tidak akan!

Dan awalnya saya juga dengan bodohnya meninggalkan ToList()panggilan itu

Andras Zoltan
sumber
Castmengembalikan sebuah IEnumerable<T>, bukan a List<T>- dan tidak, kovarian tidak akan mengizinkan konversi ini, karena tidak aman - lihat jawaban saya.
Jon Skeet
Dari laman yang Anda tautkan ke: "Hanya tipe antarmuka dan tipe delegasi yang dapat memiliki parameter tipe varian"
Jon Skeet
@Jon - Saya menyadari ToList()ada yang hilang sebelum membaca komentar Anda; tapi ya seperti yang Anda tunjukkan, tentu saja Kovarian tidak akan berhasil! Doh!
Andras Zoltan
Baik. Kovarian masih dapat membantu, karena itu berarti Anda tidak memerlukan Castpanggilan di .NET 4, selama Anda menentukan argumen type untuk ToList.
Jon Skeet
5

Saya juga mengalami masalah ini dan setelah membaca jawaban Jon Skeet saya mengubah kode saya dari List<T>use to use IEnumerable<T>. Meskipun ini tidak menjawab pertanyaan asli OP dari Bagaimana saya dapat menyerahkan List<Client>keList<IDic> , itu menghindari kebutuhan untuk melakukannya dan dengan demikian dapat membantu orang lain yang mengalami masalah ini. Ini tentu saja mengasumsikan bahwa kode yang memerlukan penggunaan List<IDic>berada di bawah kendali Anda.

Misalnya:

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

Dari pada:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}
Ɖiamond ǤeezeƦ
sumber
3

Jika Anda dapat menggunakan LINQ maka Anda dapat melakukan ini ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();
musefan
sumber
3
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();
Marc Messing
sumber
0

Ini hanya mungkin dengan membuat baru List<IDic>dan mentransfer semua elemen.

Piotr Auguscik
sumber
Ada komentar mengapa tidak dipilih, karena arti umum sama dengan semua jawaban lainnya?
Piotr Auguscik
Dugaan saya, Anda akan mendapatkan suara negatif karena Anda mengatakan itu hanya mungkin untuk membuat daftar baru, tetapi orang lain telah memposting sebaliknya ... bukan
suara negatif
Mereka memang membuat daftar baru tetapi tidak dengan operator baru yang tidak mengubah fakta yang mereka lakukan.
Piotr Auguscik
0

Di .Net 3.5, Anda dapat melakukan hal berikut:

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

Konstruktor untuk Daftar dalam hal ini membutuhkan IEnumerable.
daftar meskipun hanya dapat dikonversi ke IEnumerable. Meskipun myObj dapat dikonversi ke ISomeInterface, tipe IEnumerable tidak dapat dikonversi ke IEnumerable.

Kent Aguilar
sumber
Masalahnya adalah ini membuat salinan daftar, sebagian besar waktu Anda ingin melakukan operasi pada daftar asli
gulungan