Judul cukup mendasar, mengapa saya tidak bisa:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Judul cukup mendasar, mengapa saya tidak bisa:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Jawaban:
Komentar untuk pertanyaan awal merangkum hal ini dengan cukup baik:
karena tidak ada yang pernah merancang, menentukan, menerapkan, menguji, mendokumentasikan, dan mengirimkan fitur itu. - @Gabe Moothart
Mengapa demikian? Yah, mungkin karena perilaku penggabungan kamus tidak dapat dipikirkan dengan cara yang sesuai dengan pedoman Kerangka.
AddRangetidak ada karena rentang tidak memiliki arti apa pun bagi wadah asosiatif, karena rentang data memungkinkan entri duplikat. Misalnya jika Anda memiliki IEnumerable<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?
Addhampir selalu diimplementasikan sebagai operasi atom: berhasil dan memperbarui status koleksi, atau gagal, dan status koleksi tidak berubah. Seperti AddRangebisa gagal karena kesalahan duplikat, cara untuk menjaga perilakunya konsisten dengan Addjuga 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 AddRangeharus 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 IgnoreDuplicatesbendera ke tanda tangan?
The IgnoreDuplicatesflag (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 AddRangeuntuk 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.
AddMultipleberbeda dari AddRange, 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-masing Adddalam a try...catchdan tangkap duplikatnya dengan cara itu; atau gunakan pengindeks dan timpa nilai pertama dengan nilai selanjutnya; atau periksa terlebih dahulu menggunakan ContainsKeysebelum mencoba Add, dengan demikian mempertahankan nilai aslinya. Jika kerangka kerja memiliki metode AddRangeatau AddMultiple, 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:
Dictionary<string, string> mainDic = new Dictionary<string, string>() {
{ "Key1", "Value1" },
{ "Key2", "Value2.1" },
};
Dictionary<string, string> additionalDic= new Dictionary<string, string>() {
{ "Key2", "Value2.2" },
{ "Key3", "Value3" },
};
mainDic.AddRangeOverride(additionalDic); // Overrides all existing keys
// or
mainDic.AddRangeNewOnly(additionalDic); // Adds new keys only
// or
mainDic.AddRange(additionalDic); // Throws an error if keys already exist
// or
if (!mainDic.ContainsKeys(additionalDic.Keys)) // Checks if keys don't exist
{
mainDic.AddRange(additionalDic);
}
...
namespace MyProject.Helper
{
public static class CollectionHelper
{
public static void AddRangeOverride<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic[x.Key] = x.Value);
}
public static void AddRangeNewOnly<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => { if (!dic.ContainsKey(x.Key)) dic.Add(x.Key, x.Value); });
}
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
{
dicToAdd.ForEach(x => dic.Add(x.Key, x.Value));
}
public static bool ContainsKeys<TKey, TValue>(this IDictionary<TKey, TValue> dic, IEnumerable<TKey> keys)
{
bool result = false;
keys.ForEachOrBreak((x) => { result = dic.ContainsKey(x); return result; });
return result;
}
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
public static void ForEachOrBreak<T>(this IEnumerable<T> source, Func<T, bool> func)
{
foreach (var item in source)
{
bool result = func(item);
if (result) break;
}
}
}
}
Selamat bersenang-senang.
ToList(), kamus adalah IEnumerable<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 cari TryAdd? Akhirnya, yang kedua bisa diganti denganWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()ini bukan solusi yang baik jadi saya telah mengubah kodenya. Anda dapat menggunakan try { 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:
var combined =
dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
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.
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.
Add, selain itu hal itu bisa terjadi lebih dari sekali. Itu akan melempar sama ArgumentExceptionseperti Additu, kan?
NamedElementException??), baik dilemparkan sebagai ganti atau sebagai innerException dari ArgumentException, yang menentukan elemen bernama yang konflik ... beberapa opsi berbeda, menurut saya
Kamu bisa melakukan ini
Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);
public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
dic.Add(.., ..);
}
atau gunakan List untuk addrange dan / atau menggunakan pola di atas.
List<KeyValuePair<string, string>>
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:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
Dictionary<string, string> dic = SomeList.ToDictionary...
Jika Anda tahu Anda tidak akan memiliki kunci duplikat, Anda dapat melakukan:
dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
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.
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);?
Jangan ragu untuk menggunakan metode ekstensi seperti ini:
public static Dictionary<T, U> AddRange<T, U>(this Dictionary<T, U> destination, Dictionary<T, U> source)
{
if (destination == null) destination = new Dictionary<T, U>();
foreach (var e in source)
destination.Add(e.Key, e.Value);
return destination;
}
Berikut adalah solusi alternatif menggunakan c # 7 ValueTuples (tuple literals)
public static class DictionaryExtensions
{
public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> source, IEnumerable<ValueTuple<TKey, TValue>> kvps)
{
foreach (var kvp in kvps)
source.Add(kvp.Item1, kvp.Item2);
return source;
}
public static void AddTo<TKey, TValue>(this IEnumerable<ValueTuple<TKey, TValue>> source, Dictionary<TKey, TValue> target)
{
target.AddRange(source);
}
}
Digunakan seperti
segments
.Zip(values, (s, v) => (s.AsSpan().StartsWith("{") ? s.Trim('{', '}') : null, v))
.Where(zip => zip.Item1 != null)
.AddTo(queryParams);
Seperti yang telah disebutkan orang lain, alasan mengapa Dictionary<TKey,TVal>.AddRangetidak diterapkan adalah karena ada berbagai cara yang mungkin Anda inginkan untuk menangani kasus di mana Anda memiliki duplikat. Ini juga merupakan kasus untuk Collectionantarmuka seperti IDictionary<TKey,TVal>,ICollection<T> , dll
Hanya List<T>mengimplementasikannya, dan Anda akan mencatat bahwa IList<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:
MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));