Saya menemukan diri saya sering mencari pertanyaan online, dan banyak solusi termasuk kamus. Namun, setiap kali saya mencoba menerapkannya, saya mendapatkan bau yang mengerikan ini dalam kode saya. Misalnya setiap kali saya ingin menggunakan nilai:
int x;
if (dict.TryGetValue("key", out x)) {
DoSomethingWith(x);
}
Itu 4 baris kode untuk dasarnya melakukan hal berikut: DoSomethingWith(dict["key"])
Saya pernah mendengar bahwa menggunakan kata kunci keluar adalah pola anti karena membuat fungsi mengubah parameternya.
Juga, saya menemukan diri saya sering membutuhkan kamus "terbalik", di mana saya membalikkan kunci dan nilai.
Demikian pula, saya sering ingin mengulang-ulang item dalam kamus dan menemukan diri saya mengubah kunci atau nilai ke daftar dll untuk melakukan ini dengan lebih baik.
Saya merasa hampir selalu ada cara kamus yang lebih baik, lebih elegan, tapi saya bingung.
System.Collections.Generic
namespace tidak aman-utas .Dictionary
, yang benar-benar mereka inginkan adalah kelas baru. Bagi mereka yang 50% (hanya), ini bau desain.Jawaban:
Kamus (C # atau lainnya) hanyalah sebuah wadah tempat Anda mencari nilai berdasarkan kunci. Dalam banyak bahasa, ini lebih tepat diidentifikasi sebagai Peta dengan implementasi yang paling umum adalah HashMap.
Masalah yang harus dipertimbangkan adalah apa yang terjadi ketika kunci tidak ada. Beberapa bahasa berperilaku dengan mengembalikan
null
ataunil
atau nilai setara lainnya. Secara diam-diam menetapkan suatu nilai alih-alih memberi tahu Anda bahwa suatu nilai tidak ada.Baik atau buruk, perancang perpustakaan C # menghasilkan idiom untuk mengatasi perilaku tersebut. Mereka beralasan bahwa perilaku default untuk mencari nilai yang tidak ada adalah dengan melemparkan pengecualian. Jika Anda ingin menghindari pengecualian, maka Anda dapat menggunakan
Try
varian. Ini adalah pendekatan yang sama yang mereka gunakan untuk mem-parsing string ke integer atau objek tanggal / waktu. Intinya, dampaknya seperti ini:Dan itu dibawa ke kamus, yang pengindeks delegasinya ke
Get(index)
:Ini hanyalah cara bahasa dirancang.
Haruskah
out
variabel tidak disarankan?C # bukan bahasa pertama yang memilikinya, dan mereka memiliki tujuan mereka dalam situasi tertentu. Jika Anda mencoba membangun sistem yang sangat konkuren, maka Anda tidak bisa menggunakan
out
variabel di batas konkurensi.Dalam banyak hal, jika ada idiom yang didukung oleh penyedia perpustakaan bahasa dan inti, saya mencoba untuk mengadopsi idiom-idiom itu di API saya. Itu membuat API terasa lebih konsisten dan betah dalam bahasa itu. Jadi metode yang ditulis dalam Ruby tidak akan terlihat seperti metode yang ditulis dalam C #, C, atau Python. Mereka masing-masing memiliki cara pembuatan kode yang disukai, dan bekerja dengan itu membantu pengguna API Anda mempelajarinya lebih cepat.
Apakah Peta Secara Umum Anti-pola?
Mereka memiliki tujuan mereka, tetapi sering kali mereka mungkin menjadi solusi yang salah untuk tujuan yang Anda miliki. Terutama jika Anda memiliki pemetaan dua arah yang Anda butuhkan. Ada banyak wadah dan cara mengatur data. Ada banyak pendekatan yang dapat Anda gunakan, dan kadang-kadang Anda perlu berpikir sedikit sebelum memilih wadah itu.
Jika Anda memiliki daftar nilai pemetaan dua arah yang sangat singkat, maka Anda mungkin hanya perlu daftar tupel. Atau daftar struct, di mana Anda dapat dengan mudah menemukan kecocokan pertama di kedua sisi pemetaan.
Pikirkan domain masalah, dan pilih alat yang paling tepat untuk pekerjaan itu. Jika tidak ada, maka buatlah.
sumber
Option<T>
, maka saya pikir itu akan membuat metode ini lebih sulit untuk digunakan di C # 2.0, karena tidak memiliki pencocokan pola.Beberapa jawaban bagus di sini tentang prinsip umum dari hashtable / kamus. Tapi saya pikir saya akan menyentuh contoh kode Anda,
Pada C # 7 (yang saya pikir berusia sekitar dua tahun), yang dapat disederhanakan menjadi:
Dan tentu saja dapat dikurangi menjadi satu baris:
Jika Anda memiliki nilai default untuk saat kunci tidak ada, itu bisa menjadi:
Jadi, Anda dapat mencapai formulir ringkas dengan menggunakan penambahan bahasa yang cukup baru.
sumber
"key"
tidak ada,x
akan diinisialisasi kedefault(TValue)
"key".DoSomethingWithThis()
getOrElse("key", defaultValue)
objek Null masih pola favorit saya. Kerjakan seperti itu dan Anda tidak peduli apakahTryGetValue
mengembalikan benar atau salah.TryGetValue
tidak aman untuk atom / thread sehingga Anda dapat dengan mudah melakukan satu pemeriksaan untuk keberadaan dan lainnya untuk mengambil dan beroperasi pada nilaiIni bukan bau kode atau anti-pola, karena menggunakan fungsi gaya TryGet dengan parameter keluar adalah C # idiomatik. Namun, ada 3 opsi yang disediakan dalam C # untuk bekerja dengan Kamus, jadi sebaiknya Anda yakin Anda menggunakan yang benar untuk situasi Anda. Saya rasa saya tahu dari mana rumor bahwa ada masalah menggunakan parameter out berasal, jadi saya akan mengatasinya pada akhirnya.
Fitur apa yang digunakan ketika bekerja dengan Kamus C #:
Untuk membenarkan ini, orang hanya perlu merujuk ke dokumentasi untuk Kamus TryGetValue, di bawah "Keterangan" :
Seluruh alasan TryGetValue ada adalah untuk bertindak sebagai cara yang lebih nyaman untuk menggunakan ContainsKey dan Item [TKey], sambil menghindari keharusan mencari kamus dua kali - jadi berpura-pura tidak ada dan melakukan dua hal yang dilakukan secara manual agak canggung pilihan.
Dalam praktiknya, saya jarang menggunakan Kamus mentah, karena pepatah sederhana ini: pilih kelas / wadah paling umum yang memberi Anda fungsionalitas yang Anda butuhkan . Kamus tidak dirancang untuk mencari berdasarkan nilai daripada dengan kunci (misalnya), jadi jika itu adalah sesuatu yang Anda inginkan, mungkin lebih masuk akal untuk menggunakan struktur alternatif. Saya pikir saya mungkin telah menggunakan Kamus satu kali dalam proyek pembangunan selama setahun terakhir yang saya lakukan, hanya karena itu jarang alat yang tepat untuk pekerjaan yang saya coba lakukan. Kamus jelas bukan pisau Tentara Swiss dari kotak alat C #.
Apa yang salah dengan parameter keluar?
CA1021: Hindari parameter
Saya menduga di situlah Anda mendengar bahwa parameter keluar adalah sesuatu seperti anti-pola. Seperti halnya semua aturan, Anda harus membaca lebih dekat untuk memahami 'mengapa', dan dalam kasus ini bahkan ada penyebutan eksplisit tentang bagaimana pola Coba tidak melanggar aturan :
sumber
Setidaknya ada dua metode yang hilang dari kamus C # yang menurut saya membersihkan kode jauh dalam banyak situasi dalam bahasa lain. Yang pertama adalah mengembalikan sebuah
Option
, yang memungkinkan Anda menulis kode seperti yang berikut di Scala:Yang kedua adalah mengembalikan nilai default yang ditentukan pengguna jika kunci tidak ditemukan:
Ada sesuatu yang bisa dikatakan untuk menggunakan idiom yang disediakan bahasa saat yang tepat, seperti
Try
polanya, tetapi itu tidak berarti Anda harus hanya menggunakan apa yang disediakan bahasa. Kami adalah programmer. Tidak apa-apa untuk membuat abstraksi baru untuk membuat situasi khusus kita lebih mudah dipahami, terutama jika itu menghilangkan banyak pengulangan. Jika Anda sering membutuhkan sesuatu, seperti membalikkan pencarian atau mengulangi nilai-nilai, mewujudkannya. Buat antarmuka yang Anda inginkan.sumber
Saya setuju bahwa ini tidak berlaku. Mekanisme yang saya suka gunakan dalam kasus ini, di mana nilainya adalah tipe struct, adalah:
Sekarang kami memiliki versi baru dari
TryGetValue
pengembalian ituint?
. Kami kemudian dapat melakukan trik serupa untuk memperluasT?
:Dan sekarang kumpulkan:
dan kita sampai pada satu pernyataan yang jelas.
Saya akan sedikit kurang kuat mengatakan, dan mengatakan bahwa menghindari mutasi bila memungkinkan adalah ide yang baik.
Kemudian implementasikan atau dapatkan kamus dua arah. Mereka mudah untuk menulis, atau ada banyak implementasi yang tersedia di internet. Ada banyak implementasi di sini, misalnya:
/programming/268321/bidirectional-1-to-1-dictionary-in-c-sharp
Tentu, kita semua melakukannya.
Tanyai dirimu sendiri, "seandainya aku punya kelas selain
Dictionary
yang mengimplementasikan operasi tepat yang ingin aku lakukan; seperti apa kelas itu?" Kemudian, setelah Anda menjawab pertanyaan itu, terapkan kelas itu . Anda seorang programmer komputer. Selesaikan masalah Anda dengan menulis program komputer!sumber
Action<T>
sangat miripinterface IAction<T> { void Invoke(T t); }
, tetapi dengan aturan yang sangat lunak tentang apa yang "mengimplementasikan" antarmuka itu, dan tentang bagaimanaInvoke
mungkin dipanggil. Jika Anda ingin tahu lebih banyak, pelajari tentang "delegasi" dalam C #, dan kemudian pelajari tentang ekspresi lambda.The
TryGetValue()
membangun hanya diperlukan jika Anda tidak tahu apakah "kunci" hadir sebagai kunci dalam kamus atau tidak, jika tidakDoSomethingWith(dict["key"])
benar-benar berlaku.Pendekatan "kurang kotor" mungkin digunakan
ContainsKey()
sebagai cek.sumber
TryGetValue
Meskipun tidak optimal , setidaknya itu membuatnya sulit untuk lupa menangani kasing kosong. Idealnya, saya berharap ini hanya akan mengembalikan Opsional.ContainsKey
pendekatan untuk aplikasi multithreaded, yang merupakan waktu pemeriksaan terhadap waktu penggunaan (TOCTOU) kerentanan. Bagaimana jika beberapa utas lainnya menghapus kunci antara panggilan keContainsKey
danGetValue
?Optional<Optional<T>>
TryGetValue
diConcurrentDictionary
. Kamus reguler tidak akan disinkronkanJawaban lain berisi poin-poin bagus, jadi saya tidak akan menyatakannya kembali di sini, tetapi saya akan fokus pada bagian ini, yang sejauh ini tampaknya diabaikan:
Sebenarnya, cukup mudah untuk beralih ke kamus, karena kamus ini mengimplementasikan
IEnumerable
:Jika Anda lebih suka Linq, itu juga berhasil:
Secara keseluruhan, saya tidak menemukan Kamus sebagai antipattern - mereka hanya alat khusus dengan kegunaan khusus.
Juga, untuk lebih spesifik, periksa
SortedDictionary
(menggunakan pohon RB untuk kinerja yang lebih dapat diprediksi) danSortedList
(yang juga merupakan kamus yang membingungkan yang mengorbankan kecepatan penyisipan untuk kecepatan pencarian, tetapi bersinar jika Anda menatap dengan tampilan tetap, pra set diurutkan). Saya punya kasus di mana menggantiDictionary
denganSortedDictionary
menghasilkan urutan besarnya eksekusi lebih cepat (tapi itu bisa terjadi sebaliknya juga).sumber
Jika Anda merasa menggunakan kamus itu aneh, itu mungkin bukan pilihan yang tepat untuk masalah Anda. Kamus sangat bagus tetapi seperti yang diperhatikan oleh seorang komentator, seringkali kamus digunakan sebagai jalan pintas untuk sesuatu yang seharusnya merupakan kelas. Atau kamus itu sendiri mungkin benar sebagai metode penyimpanan inti tetapi harus ada kelas pembungkus di sekitarnya untuk menyediakan metode layanan yang diinginkan.
Banyak yang telah dikatakan tentang Dict [kunci] versus TryGet. Apa yang saya gunakan banyak adalah iterasi kamus menggunakan KeyValuePair. Rupanya ini adalah konstruksi yang kurang dikenal.
Manfaat utama kamus adalah sangat cepat dibandingkan dengan koleksi lain karena jumlah item bertambah. Jika Anda harus memeriksa apakah kunci ada banyak, Anda mungkin ingin bertanya pada diri sendiri apakah penggunaan kamus Anda sesuai. Sebagai klien Anda biasanya harus tahu apa yang Anda masukkan ke sana dan dengan demikian apa yang akan aman untuk ditanyakan.
sumber