Berasal dari latar belakang C # dan Java, saya terbiasa dengan daftar saya yang homogen, dan itu masuk akal bagi saya. Ketika saya mulai mengambil Lisp, saya perhatikan bahwa daftar itu bisa heterogen. Ketika saya mulai mencari-cari dengan dynamic
kata kunci dalam C #, saya perhatikan bahwa, pada C # 4.0, ada juga daftar yang heterogen:
List<dynamic> heterogeneousList
Pertanyaan saya, apa gunanya? Sepertinya daftar yang heterogen akan memiliki lebih banyak overhead saat melakukan pemrosesan dan bahwa jika Anda perlu menyimpan berbagai jenis di satu tempat Anda mungkin memerlukan struktur data yang berbeda. Apakah kenaifan saya merawat wajahnya yang jelek atau apakah benar-benar ada saatnya berguna untuk memiliki daftar yang heterogen?
List<dynamic>
perbedaan (untuk pertanyaan Anda) dari sekadar melakukanList<object>
?Jawaban:
Makalah Sangat Diketik Koleksi Heterogen oleh Oleg Kiselyov, Ralf Lämmel, dan Keean Schupke tidak hanya berisi implementasi daftar heterogen di Haskell, tetapi juga contoh memotivasi kapan, mengapa dan bagaimana Anda akan menggunakan HLists. Secara khusus, mereka menggunakannya untuk akses basis data yang diperiksa jenis kompilasi-waktu aman. (Pikirkan LINQ, pada kenyataannya, kertas yang mereka rujuk adalah kertas Haskell oleh Erik Meijer et al yang mengarah ke LINQ.)
Mengutip dari paragraf pengantar dari makalah HLists:
Perhatikan bahwa contoh yang Anda berikan dalam pertanyaan Anda benar-benar bukan daftar yang heterogen dalam arti bahwa kata tersebut umum digunakan. Mereka lemah diketik atau untyped daftar. Faktanya, mereka sebenarnya adalah daftar yang homogen , karena semua elemen memiliki tipe yang sama:
object
ataudynamic
. Anda kemudian dipaksa untuk melakukan gips atauinstanceof
tes yang tidak dicentang atau sesuatu seperti itu, agar benar-benar dapat bekerja secara bermakna dengan elemen-elemen, yang membuat mereka diketik dengan lemah.sumber
Singkatnya, kontainer yang heterogen memperdagangkan kinerja runtime untuk fleksibilitas. Jika Anda ingin memiliki "daftar barang" tanpa memperhatikan jenis barang tertentu, heterogenitas adalah jalan yang harus ditempuh. Lisps secara karakteristik diketik secara dinamis, dan sebagian besar semuanya merupakan daftar nilai kotak, jadi kinerja hit yang lebih kecil diharapkan. Di dunia Lisp, produktivitas programmer dianggap lebih penting daripada kinerja runtime.
Dalam bahasa yang diketik secara dinamis, wadah yang homogen sebenarnya memiliki sedikit overhead dibandingkan dengan yang heterogen, karena semua elemen yang ditambahkan harus diperiksa jenisnya.
Intuisi Anda tentang memilih struktur data yang lebih baik tepat sasaran. Secara umum, semakin banyak kontrak yang dapat Anda tempatkan pada kode Anda, semakin banyak Anda tahu tentang cara kerjanya, dan semakin dapat diandalkan, dipelihara, & c. menjadi. Namun, kadang-kadang Anda benar-benar menginginkan wadah yang heterogen, dan Anda harus diizinkan memilikinya jika Anda membutuhkannya.
sumber
IUserSetting
dan mengimplementasikannya beberapa kali lipat, atau membuat generikUserSetting<T>
, tetapi salah satu masalah dengan pengetikan statis adalah Anda mendefinisikan antarmuka sebelum Anda tahu persis bagaimana itu akan digunakan. Hal-hal yang Anda lakukan dengan pengaturan integer mungkin sangat berbeda dari hal-hal yang Anda lakukan dengan pengaturan string, jadi operasi apa yang masuk akal untuk dimasukkan ke dalam antarmuka umum? Sampai Anda tahu pasti, lebih baik menggunakan pengetikan dinamis, dan membuatnya konkret nanti.object
daripadadynamic
, maka tentu saja, gunakan yang pertama.Dalam bahasa fungsional (seperti lisp), Anda menggunakan pencocokan pola untuk menentukan apa yang terjadi pada elemen tertentu dalam daftar. Setara dalam C # akan menjadi rantai if ... else jika pernyataan yang memeriksa jenis elemen dan melakukan operasi berdasarkan itu. Tidak perlu dikatakan, pencocokan pola fungsional lebih efisien daripada pengecekan tipe runtime.
Menggunakan polimorfisme akan lebih cocok dengan pencocokan pola. Artinya, memiliki objek daftar cocok dengan antarmuka tertentu dan memanggil fungsi pada antarmuka itu untuk setiap objek. Alternatif lain adalah menyediakan serangkaian metode kelebihan beban yang menggunakan jenis objek tertentu sebagai parameter. Metode default mengambil Objek sebagai parameternya.
Pendekatan ini memberikan perkiraan untuk pencocokan pola Lisp. Pola pengunjung (seperti yang diterapkan di sini, adalah contoh yang bagus dari penggunaan untuk daftar heterogen). Contoh lain akan untuk pengiriman pesan di mana, ada pendengar untuk pesan tertentu dalam antrian prioritas dan menggunakan rantai tanggung jawab, pengirim melewati pesan dan penangan pertama yang cocok dengan pesan menanganinya.
Sisi lain memberitahu semua orang yang mendaftar untuk pesan (misalnya pola Agregator Acara yang biasa digunakan untuk kopling longgar ViewModels dalam pola MVVM). Saya menggunakan konstruksi berikut
Satu-satunya cara untuk menambahkan ke kamus adalah fungsi
(dan objek tersebut sebenarnya adalah WeakReference ke yang diteruskan dalam handler). Jadi di sini saya HARUS menggunakan Daftar <Object> karena pada waktu kompilasi, saya tidak tahu apa tipe yang akan ditutup. Namun pada Runtime saya dapat memastikan bahwa itu adalah Jenis yang merupakan kunci untuk kamus. Ketika saya ingin memecat acara yang saya sebut
dan sekali lagi saya menyelesaikan daftar. Tidak ada keuntungan menggunakan Daftar <dynamic> karena saya harus tetap menggunakannya. Jadi seperti yang Anda lihat ada manfaat untuk kedua pendekatan. Jika Anda akan secara dinamis mengirimkan objek menggunakan Metode overloading, dinamis adalah cara untuk melakukannya. Jika Anda dipaksa untuk membuang apa pun, mungkin juga menggunakan Object.
sumber
DoSomething(Object)
(setidaknya saat menggunakanobject
dalamforeach
loop;dynamic
adalah hal yang sama sekali berbeda).Anda benar bahwa heterogenitas membawa overhead runtime, tetapi yang lebih penting itu melemahkan jaminan waktu kompilasi yang disediakan oleh typechecker. Meski begitu, ada beberapa masalah di mana alternatifnya bahkan lebih mahal.
Dalam pengalaman saya, berurusan dengan byte mentah melalui file, soket jaringan, dll, Anda sering mengalami masalah seperti itu.
Untuk memberikan contoh nyata, pertimbangkan sistem perhitungan terdistribusi menggunakan futures . Seorang pekerja pada sebuah simpul individu dapat menelurkan pekerjaan dari jenis serial apapun, menghasilkan masa depan dari jenis itu. Di belakang layar, sistem mengirimkan pekerjaan ke rekan, dan kemudian menyimpan catatan yang menghubungkan unit kerja itu dengan masa depan tertentu yang harus diisi begitu jawaban untuk pekerjaan itu kembali.
Di mana catatan ini bisa disimpan? Secara intuitif, yang Anda inginkan adalah sesuatu seperti
Dictionary<WorkId, Future<TValue>>
, tetapi ini membatasi Anda untuk mengelola hanya satu jenis masa depan di seluruh sistem. Jenis yang lebih cocok adalahDictionary<WorkId, Future<dynamic>>
, karena pekerja dapat memilih jenis yang sesuai ketika memaksa masa depan.Catatan : Contoh ini berasal dari dunia Haskell tempat kami tidak memiliki subtyping. Saya tidak akan terkejut jika ada solusi yang lebih idiomatis untuk contoh khusus ini dalam C #, tapi mudah-mudahan masih ilustratif.
sumber
ISTR yang Lisp tidak memiliki struktur data selain daftar, jadi jika Anda memerlukan segala jenis objek data agregat, itu harus menjadi daftar yang heterogen. Seperti yang telah ditunjukkan orang lain, mereka juga berguna untuk membuat serialisasi data untuk transmisi atau penyimpanan. Salah satu fitur yang bagus adalah bahwa mereka juga bersifat terbuka, sehingga Anda dapat menggunakannya dalam suatu sistem berdasarkan analogi pipa-dan-filter dan memiliki langkah-langkah pemrosesan yang berurutan menambah atau memperbaiki data tanpa memerlukan objek data tetap atau topologi alur kerja .
sumber