Dalam kode saya perlu menggunakan IEnumerable<>
beberapa kali sehingga mendapatkan kesalahan Resharper dari "Kemungkinan beberapa enumerasi IEnumerable
".
Kode sampel:
public List<object> Foo(IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
throw new ArgumentException();
var firstObject = objects.First();
var list = DoSomeThing(firstObject);
var secondList = DoSomeThingElse(objects);
list.AddRange(secondList);
return list;
}
- Saya dapat mengubah
objects
parameter menjadiList
dan kemudian menghindari kemungkinan beberapa enumerasi tetapi kemudian saya tidak mendapatkan objek tertinggi yang bisa saya tangani. - Hal lain yang dapat saya lakukan adalah mengonversikan
IEnumerable
keList
di awal metode:
public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
// ...
}
Tapi ini hanya canggung .
Apa yang akan Anda lakukan dalam skenario ini?
sumber
IReadOnlyCollection(T)
(baru dengan .net 4.5) sebagai antarmuka terbaik untuk menyampaikan gagasan bahwa itu adalahIEnumerable(T)
yang dimaksudkan untuk disebutkan berulang kali. Seperti yang dinyatakan oleh jawaban ini,IEnumerable(T)
itu sendiri sangat umum sehingga bahkan merujuk pada konten yang tidak dapat disetel ulang yang tidak dapat dihitung ulang tanpa efek samping. TetapiIReadOnlyCollection(T)
menyiratkan re-iterability..ToList()
di awal metode, Anda harus terlebih dahulu memeriksa apakah parameternya tidak nol. Itu berarti Anda akan mendapatkan dua tempat di mana Anda melempar ArgumentException: jika parameternya nol dan jika tidak memiliki item apa pun.IReadOnlyCollection<T>
vsIEnumerable<T>
dan kemudian memposting pertanyaan tentang penggunaanIReadOnlyCollection<T>
sebagai parameter, dan dalam hal ini. Saya pikir kesimpulan saya akhirnya sampai pada logika untuk diikuti. Itu harus digunakan di mana pencacahan diharapkan, tidak harus di mana mungkin ada pencacahan ganda.Jika data Anda selalu dapat diulang, mungkin jangan khawatir tentang hal itu. Namun, Anda juga dapat membuka gulungannya - ini sangat berguna jika data yang masuk bisa besar (misalnya, membaca dari disk / jaringan):
Catatan Saya mengubah semantik DoSomethingElse sedikit, tetapi ini terutama untuk menunjukkan penggunaan yang belum dibuka. Anda bisa membungkus kembali iterator, misalnya. Anda bisa membuatnya menjadi blok iterator juga, yang bisa jadi bagus; maka tidak ada
list
- dan Anda akanyield return
item yang Anda dapatkan, daripada menambah daftar untuk dikembalikan.sumber
Menggunakan
IReadOnlyCollection<T>
atauIReadOnlyList<T>
dalam metode tanda tangan alih-alihIEnumerable<T>
, memiliki keuntungan membuat eksplisit bahwa Anda mungkin perlu memeriksa penghitungan sebelum mengulangi, atau mengulangi beberapa kali karena alasan lain.Namun mereka memiliki kelemahan besar yang akan menyebabkan masalah jika Anda mencoba untuk memperbaiki kode Anda untuk menggunakan antarmuka, misalnya untuk membuatnya lebih dapat diuji dan ramah terhadap proxy dinamis. Poin kuncinya adalah yang
IList<T>
tidak diwariskan dariIReadOnlyList<T>
, dan juga untuk koleksi lain dan masing-masing antarmuka read-only. (Singkatnya, ini karena .NET 4.5 ingin menjaga kompatibilitas ABI dengan versi sebelumnya. Tetapi mereka bahkan tidak mengambil kesempatan untuk mengubahnya di .NET core. )Ini berarti bahwa jika Anda mendapatkan
IList<T>
dari beberapa bagian dari program dan ingin meneruskannya ke bagian lain yang mengharapkanIReadOnlyList<T>
, Anda tidak bisa! Namun Anda dapat lulusIList<T>
sebagaiIEnumerable<T>
.Pada akhirnya,
IEnumerable<T>
adalah satu-satunya antarmuka read-only yang didukung oleh semua koleksi .NET termasuk semua antarmuka koleksi. Alternatif lain akan kembali menggigit Anda ketika Anda menyadari bahwa Anda mengunci diri dari beberapa pilihan arsitektur. Jadi saya pikir ini adalah tipe yang tepat untuk digunakan dalam tanda tangan fungsi untuk menyatakan bahwa Anda hanya ingin koleksi read-only.(Perhatikan bahwa Anda selalu dapat menulis
IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)
metode ekstensi yang dicetak sederhana jika tipe yang mendasarinya mendukung kedua antarmuka, tetapi Anda harus menambahkannya secara manual di mana-mana ketika melakukan refactoring, di mana sepertiIEnumerable<T>
yang selalu kompatibel.)Seperti biasa ini tidak mutlak, jika Anda menulis kode database-berat di mana penghitungan berulang secara tidak disengaja akan menjadi bencana, Anda mungkin lebih suka trade-off yang berbeda.
sumber
Jika tujuannya adalah benar-benar untuk mencegah beberapa enumerasi daripada jawaban oleh Marc Gravell adalah yang dibaca, tetapi mempertahankan semantik yang sama Anda dapat dengan mudah menghapus redundan
Any
danFirst
panggilan dan pergi dengan:Perhatikan, bahwa ini mengasumsikan bahwa Anda
IEnumerable
tidak generik atau setidaknya dibatasi menjadi tipe referensi.sumber
objects
masih diteruskan ke DoSomethingElse yang mengembalikan daftar, sehingga kemungkinan Anda masih menghitung dua kali masih ada. Either way jika koleksi adalah permintaan LINQ untuk SQL, Anda menekan DB dua kali.First
akan melempar pengecualian untuk daftar kosong. Saya tidak berpikir mungkin untuk mengatakan tidak pernah membuang pengecualian pada daftar kosong atau selalu membuang pengecualian pada daftar kosong. Tergantung ...Saya biasanya membebani metode saya dengan IEnumerable dan IList dalam situasi ini.
Saya berhati-hati untuk menjelaskan dalam ringkasan komentar tentang metode yang memanggil IEnumerable akan melakukan .ToList ().
Pemrogram dapat memilih untuk .ToList () pada tingkat yang lebih tinggi jika beberapa operasi sedang digabungkan dan kemudian memanggil kelebihan IList atau membiarkan kelebihan IEnumerable saya yang mengurusnya.
sumber
Jika Anda hanya perlu memeriksa elemen pertama, Anda dapat mengintipnya tanpa mengulangi seluruh koleksi:
sumber
Pertama, peringatan itu tidak selalu berarti banyak. Saya biasanya menonaktifkannya setelah memastikan itu bukan leher botol kinerja. Itu hanya berarti
IEnumerable
dievaluasi dua kali, yang biasanya tidak menjadi masalah kecuali jikaevaluation
itu sendiri membutuhkan waktu lama. Bahkan jika itu memakan waktu lama, dalam hal ini Anda hanya menggunakan satu elemen pertama kali.Dalam skenario ini Anda juga dapat mengeksploitasi metode ekstensi LINQ yang kuat bahkan lebih.
Dimungkinkan untuk hanya mengevaluasi
IEnumerable
sekali dalam kasus ini dengan beberapa kerumitan, tetapi profil terlebih dahulu dan lihat apakah itu benar-benar masalah.sumber
IEnumerables
yang memberikan hasil yang berbeda setiap kali Anda mengulanginya atau yang membutuhkan waktu yang lama untuk beralih. Lihat jawaban Marc Gravells - Dia juga menyatakan pertama dan terutama "mungkin jangan khawatir". Masalahnya adalah - Banyak kali Anda melihat ini, Anda tidak perlu khawatir.