Saya baru saja membaca artikel yang menarik berjudul Getting too cute with c # yield return
Itu membuat saya bertanya-tanya apa cara terbaik untuk mendeteksi apakah IEnumerable adalah kumpulan enumerable yang sebenarnya, atau apakah itu mesin negara yang dihasilkan dengan kata kunci hasil.
Misalnya, Anda bisa memodifikasi DoubleXValue (dari artikel) menjadi sesuatu seperti:
private void DoubleXValue(IEnumerable<Point> points)
{
if(points is List<Point>)
foreach (var point in points)
point.X *= 2;
else throw YouCantDoThatException();
}
Pertanyaan 1) Apakah ada cara yang lebih baik untuk melakukan ini?
Pertanyaan 2) Apakah ini sesuatu yang harus saya khawatirkan ketika membuat API?
c#
api-design
ConditionRacer
sumber
sumber
ICollection<T>
akan menjadi pilihan yang lebih baik karena tidak semua koleksiList<T>
. Misalnya, arrayPoint[]
implementIList<T>
tetapi tidakList<T>
.IEnumerable
tidak dapat diubah dalam arti bahwa Anda tidak dapat memodifikasi koleksi (menambah / menghapus) ketika menghitungnya, namun jika objek dalam daftar dapat berubah, sangat masuk akal untuk memodifikasi mereka saat menghitung daftar.ToList
? msdn.microsoft.com/en-us/library/bb342261.aspx Itu tidak mendeteksi apakah itu dihasilkan oleh hasil, tetapi malah membuatnya tidak relevan.Jawaban:
Pertanyaan Anda, seperti yang saya mengerti, tampaknya didasarkan pada premis yang salah. Biarkan saya melihat apakah saya dapat merekonstruksi alasannya:
Masalahnya adalah premis kedua salah. Bahkan jika Anda dapat mendeteksi apakah IEnumerable yang diberikan adalah hasil dari transformasi blok iterator (dan ya, ada cara untuk melakukan itu) tidak akan membantu karena anggapan tersebut salah. Mari kita ilustrasikan alasannya.
Baiklah, kita punya empat metode. S1 dan S2 adalah urutan yang dihasilkan secara otomatis; S3 dan S4 adalah urutan yang dibuat secara manual. Sekarang misalkan kita memiliki:
Hasil untuk S1 dan S4 akan menjadi 0; setiap kali Anda menyebutkan urutannya, Anda mendapatkan referensi baru ke huruf M yang dibuat. Hasil untuk S2 dan S3 adalah 100; setiap kali Anda menyebutkan urutannya, Anda mendapatkan referensi yang sama dengan M yang Anda dapatkan terakhir kali.Apakah kode urutan dihasilkan secara otomatis atau tidak adalah ortogonal terhadap pertanyaan apakah objek yang disebutkan memiliki identitas referensial atau tidak. Dua properti itu - pembangkitan otomatis dan identitas referensial - sebenarnya tidak ada hubungannya satu sama lain. Artikel yang Anda tautkan agak mengkonfigurasinya.
Kecuali penyedia urutan didokumentasikan sebagai selalu menawarkan benda-benda yang memiliki identitas referensial , tidak bijaksana untuk berasumsi bahwa ia melakukannya.
sumber
Saya pikir konsep kuncinya adalah bahwa Anda harus memperlakukan
IEnumerable<T>
koleksi apa pun sebagai tidak berubah : Anda tidak boleh memodifikasi objek dari itu, Anda harus membuat koleksi baru dengan objek baru:(Atau tulis yang sama lebih ringkas menggunakan LINQ
Select()
.)Jika Anda benar-benar ingin memodifikasi item dalam koleksi, jangan gunakan
IEnumerable<T>
, gunakanIList<T>
(atau mungkinIReadOnlyList<T>
jika Anda tidak ingin mengubah koleksi itu sendiri, hanya item di dalamnya, dan Anda menggunakan .Net 4.5):Dengan cara ini, jika seseorang mencoba menggunakan metode Anda
IEnumerable<T>
, itu akan gagal pada waktu kompilasi.sumber
IEnumerable
lebih dari sekali. Kebutuhan untuk melakukannya lebih dari sekali dapat dihilangkan dengan meneleponToList()
dan mengulangi daftar, meskipun dalam kasus khusus yang ditunjukkan oleh OP saya lebih suka solusi svick memiliki DoubleXValue juga mengembalikan IEnumerable. Tentu saja, dalam kode nyata saya mungkin akan menggunakanLINQ
untuk menghasilkan jenis iniIEnumerable
. Namun, pilihan svickyield
masuk akal dalam konteks pertanyaan OP.Select()
. Dan mengenai beberapa iterasi dariIEnumerable
: ReSharper sangat membantu dengan itu, karena itu dapat memperingatkan Anda ketika Anda melakukan itu.Saya pribadi berpikir masalah yang disoroti dalam artikel itu berasal dari terlalu sering menggunakan
IEnumerable
antarmuka oleh banyak pengembang C # hari ini. Tampaknya telah menjadi tipe default ketika Anda membutuhkan koleksi objek - tetapi ada banyak informasi yangIEnumerable
tidak memberi tahu Anda ... yang terpenting: apakah sudah diverifikasi * atau tidak.Jika Anda menemukan bahwa Anda perlu mendeteksi apakah
IEnumerable
benar-benar sebuahIEnumerable
atau sesuatu yang lain, abstraksi telah bocor dan Anda mungkin dalam situasi di mana tipe parameter Anda agak terlalu longgar. Jika Anda memerlukan informasi tambahan yangIEnumerable
tidak disediakan sendiri, buat persyaratan itu eksplisit dengan memilih salah satu antarmuka lain sepertiICollection
atauIList
.Edit untuk downvoters Harap kembali dan baca pertanyaan dengan seksama. OP meminta cara untuk memastikan bahwa
IEnumerable
instance telah terwujud sebelum diteruskan ke metode API-nya. Jawaban saya menjawab pertanyaan itu. Ada masalah yang lebih luas tentang cara penggunaan yang benarIEnumerable
, dan tentu saja harapan kode yang ditempelkan OP didasarkan pada kesalahpahaman tentang masalah yang lebih luas itu, tetapi OP tidak bertanya bagaimana menggunakan dengan benarIEnumerable
, atau bagaimana bekerja dengan jenis yang tidak dapat diubah . Tidak adil untuk menurunkan saya karena tidak menjawab pertanyaan yang tidak diajukan.* Saya menggunakan istilah
reify
, yang lain menggunakanstabilize
ataumaterialize
. Saya tidak berpikir konvensi umum telah diadopsi oleh komunitas dulu.sumber
ICollection
danIList
adalah bahwa mereka bisa berubah (atau setidaknya terlihat seperti itu).IReadOnlyCollection
danIReadOnlyList
dari .Net 4.5 memecahkan beberapa masalah tersebut.