Judul cukup mendasar, mengapa saya tidak bisa:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
c#
.net
dictionary
Custodio
sumber
sumber
Jawaban:
Komentar untuk pertanyaan awal merangkum hal ini dengan cukup baik:
Mengapa demikian? Yah, mungkin karena perilaku penggabungan kamus tidak dapat dipikirkan dengan cara yang sesuai dengan pedoman Kerangka.
AddRange
tidak ada karena rentang tidak memiliki arti apa pun bagi wadah asosiatif, karena rentang data memungkinkan entri duplikat. Misalnya jika Anda memilikiIEnumerable<KeyValuePair<K,T>>
koleksi yang tidak melindungi dari entri duplikat.Perilaku menambahkan kumpulan pasangan nilai kunci, atau bahkan menggabungkan dua kamus sangatlah mudah. Perilaku bagaimana menangani beberapa entri duplikat, bagaimanapun, tidak.
Apa yang harus menjadi perilaku metode ketika berurusan dengan duplikat?
Setidaknya ada tiga solusi yang dapat saya pikirkan:
Ketika pengecualian dilempar, bagaimana seharusnya keadaan kamus aslinya?
Add
hampir selalu diimplementasikan sebagai operasi atom: berhasil dan memperbarui status koleksi, atau gagal, dan status koleksi tidak berubah. SepertiAddRange
bisa gagal karena kesalahan duplikat, cara untuk menjaga perilakunya konsisten denganAdd
juga membuatnya atomic dengan melemparkan pengecualian pada duplikat apa pun, dan membiarkan status kamus asli tidak berubah.Sebagai konsumen API, akan membosankan jika harus menghapus elemen duplikat secara berulang, yang menyiratkan bahwa
AddRange
harus melontarkan satu pengecualian yang berisi semua nilai duplikat.Pilihannya kemudian bermuara pada:
Ada argumen untuk mendukung kedua kasus penggunaan tersebut. Untuk melakukan itu, apakah Anda menambahkan sebuah
IgnoreDuplicates
bendera ke tanda tangan?The
IgnoreDuplicates
flag (ketika diatur ke true) juga akan memberikan kecepatan yang signifikan atas, sebagai implementasi yang mendasari akan bypass kode untuk duplikat memeriksa.Jadi sekarang, Anda memiliki bendera yang memungkinkan
AddRange
untuk mendukung kedua kasus, tetapi memiliki efek samping yang tidak terdokumentasi (yang merupakan sesuatu yang desainer Framework bekerja sangat keras untuk dihindari).Ringkasan
Karena tidak ada perilaku yang jelas, konsisten, dan diharapkan saat berurusan dengan duplikat, lebih mudah untuk tidak menangani semuanya bersama-sama, dan tidak menyediakan metode untuk memulai.
Jika Anda mendapati diri Anda terus-menerus harus menggabungkan kamus, Anda tentu saja dapat menulis metode ekstensi Anda sendiri untuk menggabungkan kamus, yang akan berperilaku sesuai dengan aplikasi Anda.
sumber
AddMultiple
berbeda dariAddRange
, terlepas dari implementasinya akan menjadi miring: Apakah Anda membuang pengecualian dengan array dari semua kunci duplikat? Atau apakah Anda memberikan pengecualian pada kunci duplikat pertama yang Anda temui? Bagaimana seharusnya keadaan kamus jika pengecualian dilempar? Murni, atau semua kunci yang berhasil?Add
- baik bungkus masing-masingAdd
dalam atry...catch
dan tangkap duplikatnya dengan cara itu; atau gunakan pengindeks dan timpa nilai pertama dengan nilai selanjutnya; atau periksa terlebih dahulu menggunakanContainsKey
sebelum mencobaAdd
, dengan demikian mempertahankan nilai aslinya. Jika kerangka kerja memiliki metodeAddRange
atauAddMultiple
, satu-satunya cara sederhana untuk mengkomunikasikan apa yang telah terjadi adalah melalui pengecualian, dan penanganan serta pemulihan yang terlibat tidak kalah rumitnya.Saya punya beberapa solusi:
...
Selamat bersenang-senang.
sumber
ToList()
, kamus adalahIEnumerable<KeyValuePair<TKey,TValue>
. Selain itu, metode kedua dan ketiga akan dibuang jika Anda menambahkan nilai kunci yang ada. Bukan ide yang bagus, apa yang Anda cariTryAdd
? Akhirnya, yang kedua bisa diganti denganWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
ini bukan solusi yang baik jadi saya telah mengubah kodenya. Anda dapat menggunakantry { mainDic.AddRange(addDic); } catch { do something }
jika Anda tidak yakin untuk metode ketiga. Metode kedua bekerja dengan sempurna.Jika seseorang menemukan pertanyaan ini seperti saya - itu mungkin untuk mencapai "AddRange" dengan menggunakan metode ekstensi IEnumerable:
Trik utama saat menggabungkan kamus adalah berurusan dengan kunci duplikat. Dalam kode di atas itu adalah bagian
.Select(grp => grp.First())
. Dalam hal ini, ini hanya mengambil elemen pertama dari grup duplikat tetapi Anda dapat menerapkan logika yang lebih canggih di sana jika diperlukan.sumber
dict1
tidak menggunakan pembanding kesetaraan default?var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Dugaan saya adalah kurangnya output yang tepat bagi pengguna tentang apa yang terjadi. Karena Anda tidak dapat memiliki kunci berulang dalam kamus, bagaimana Anda menangani penggabungan dua kamus di mana beberapa kunci berpotongan? Tentu Anda bisa mengatakan: "Saya tidak peduli" tapi itu melanggar konvensi mengembalikan false / melempar pengecualian untuk kunci berulang.
sumber
Add
, selain itu hal itu bisa terjadi lebih dari sekali. Itu akan melempar samaArgumentException
sepertiAdd
itu, kan?NamedElementException
??), baik dilemparkan sebagai ganti atau sebagai innerException dari ArgumentException, yang menentukan elemen bernama yang konflik ... beberapa opsi berbeda, menurut sayaKamu bisa melakukan ini
atau gunakan List untuk addrange dan / atau menggunakan pola di atas.
sumber
MethodThatReturnAnotherDic
. Itu berasal dari OP. Harap tinjau kembali pertanyaan dan jawaban saya.Jika Anda berurusan dengan Dictionary baru (dan Anda tidak memiliki baris yang hilang), Anda selalu dapat menggunakan ToDictionary () dari daftar objek lain.
Jadi, dalam kasus Anda, Anda akan melakukan sesuatu seperti ini:
sumber
Dictionary<string, string> dic = SomeList.ToDictionary...
Jika Anda tahu Anda tidak akan memiliki kunci duplikat, Anda dapat melakukan:
Ini akan memunculkan pengecualian jika ada pasangan kunci / nilai duplikat.
Saya tidak tahu mengapa ini tidak ada dalam kerangka; seharusnya. Tidak ada ketidakpastian; lempar saja pengecualian. Dalam kasus kode ini, itu membuat pengecualian.
sumber
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
?Jangan ragu untuk menggunakan metode ekstensi seperti ini:
sumber
Berikut adalah solusi alternatif menggunakan c # 7 ValueTuples (tuple literals)
Digunakan seperti
sumber
Seperti yang telah disebutkan orang lain, alasan mengapa
Dictionary<TKey,TVal>.AddRange
tidak diterapkan adalah karena ada berbagai cara yang mungkin Anda inginkan untuk menangani kasus di mana Anda memiliki duplikat. Ini juga merupakan kasus untukCollection
antarmuka sepertiIDictionary<TKey,TVal>
,ICollection<T>
, dllHanya
List<T>
mengimplementasikannya, dan Anda akan mencatat bahwaIList<T>
antarmuka tidak, karena alasan yang sama: diharapkan perilaku yang saat menambahkan rentang nilai ke koleksi dapat sangat bervariasi, bergantung pada konteks.Konteks pertanyaan Anda menyarankan Anda tidak khawatir tentang duplikat, dalam hal ini Anda memiliki alternatif satu jalur yang sederhana, menggunakan Linq:
sumber