Başlık yeterince basit, neden yapamıyorum:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Başlık yeterince basit, neden yapamıyorum:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Yanıtlar:
Orijinal soruya yapılan bir yorum bunu oldukça iyi özetliyor:
çünkü hiç kimse bu özelliği tasarlamadı, belirledi, uyguladı, test etmedi, belgelendirmedi ve göndermedi. - @Gabe Moothart
Neden? Muhtemelen sözlükleri birleştirme davranışı Çerçeve yönergelerine uyacak şekilde gerekçelendirilemez.
AddRange
Veri aralığı yinelenen girişlere izin verdiğinden, bir aralığın ilişkili kapsayıcı için herhangi bir anlamı olmadığı için mevcut değildir. Örneğin, bir IEnumerable<KeyValuePair<K,T>>
koleksiyonunuz varsa, yinelenen girişlere karşı koruma sağlamaz.
Bir anahtar-değer çifti koleksiyonu ekleme veya hatta iki sözlüğü birleştirme davranışı basittir. Bununla birlikte, birden çok yinelenen girişle nasıl başa çıkılacağı davranışı değildir.
Bir kopya ile uğraşırken yöntemin davranışı ne olmalıdır?
Aklıma gelen en az üç çözüm var:
Bir istisna atıldığında, orijinal sözlüğün durumu ne olmalıdır?
Add
neredeyse her zaman atomik bir işlem olarak uygulanır: koleksiyonun durumunu başarılı olur ve günceller veya başarısız olur ve koleksiyonun durumu değişmeden kalır. AddRange
Yinelenen hatalar nedeniyle başarısız olabileceği gibi , davranışını tutarlı tutmanın yolu Add
, herhangi bir kopyaya bir istisna atarak atomik hale getirmek ve orijinal sözlüğün durumunu değiştirmeden bırakmak olacaktır.
Bir API tüketicisi olarak, yinelenen öğeleri yinelemeli olarak kaldırmak zorunda kalmak sıkıcı olacaktır, bu da tüm yinelenen değerleri AddRange
içeren tek bir istisna atması gerektiği anlamına gelir .
Seçim daha sonra şu şekilde özetlenir:
Her iki kullanım durumunu da destekleyen argümanlar vardır. Bunu yapmak IgnoreDuplicates
için imzaya bir bayrak ekler misiniz ?
IgnoreDuplicates
(True olarak ayarlanmış) bayrağı da altta yatan uygulama olarak, önemli bir hız sağlayacaktır olur yinelenen denetimi için kod baypas.
Şimdi, AddRange
her iki durumu da desteklemeye izin veren , ancak belgelenmemiş bir yan etkisi olan bir bayrağınız var (bu, Çerçeve tasarımcılarının kaçınmak için gerçekten çok çalıştığı bir şeydi).
özet
Yinelenenlerle uğraşmak söz konusu olduğunda net, tutarlı ve beklenen bir davranış olmadığından, bunların hepsini bir arada ele almamak ve başlangıç için bir yöntem sağlamamak daha kolaydır.
Kendinizi sürekli olarak sözlükleri birleştirmek zorunda bulursanız, elbette sözlükleri birleştirmek için kendi uzantı yönteminizi yazabilirsiniz; bu, uygulamalarınız için işe yarayacak şekilde davranacaktır.
AddMultiple
uygulamasından farklıdır AddRange
, uygulaması riskli olacaktır: Tüm yinelenen anahtarların bir dizisiyle bir istisna mı atarsınız ? Yoksa karşılaştığınız ilk yinelenen anahtara bir istisna mı atarsınız? Bir istisna atılırsa sözlüğün durumu ne olmalıdır? Bozulmamış mı, yoksa başarılı olan tüm anahtarlar mı?
Add
- Ya her sarın Add
bir de try...catch
ve yol olduğunu çiftleri yakalamak; veya indeksleyiciyi kullanın ve ilk değerin üzerine daha sonraki değeri yazın; veya ContainsKey
denemeden önce kullanmayı önceden kontrol edin Add
, böylece orijinal değeri koruyun. Çerçevenin bir AddRange
veya AddMultiple
yöntemi olsaydı , ne olduğunu anlatmanın tek basit yolu bir istisna olabilirdi ve ilgili işleme ve kurtarma daha az karmaşık olmazdı.
Bir çözümüm var:
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;
}
}
}
}
İyi eğlenceler.
ToList()
, sözlük bir IEnumerable<KeyValuePair<TKey,TValue>
. Ayrıca, mevcut bir anahtar değeri eklerseniz ikinci ve üçüncü yöntem yöntemleri atar. İyi bir fikir değil mi arıyordun TryAdd
? Son olarak, ikincisiWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
iyi bir çözüm değil , bu yüzden kodu değiştirdim. try { mainDic.AddRange(addDic); } catch { do something }
Üçüncü yöntemden emin değilseniz kullanabilirsiniz . İkinci yöntem mükemmel çalışıyor.
Benim gibi birinin bu soruyla karşılaşması durumunda - IEnumerable uzantı yöntemlerini kullanarak "AddRange" elde etmek mümkündür:
var combined =
dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Sözlükleri birleştirirken ana numara, yinelenen anahtarlarla uğraşmaktır. Yukarıdaki kodda bu kısım .Select(grp => grp.First())
. Bu durumda, kopya grubundan basitçe ilk öğeyi alır, ancak gerekirse orada daha karmaşık bir mantık uygulayabilirsiniz.
dict1
gelmez varsayılan eşitlik karşılaştırıcısı kullanabilir?
var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Tahminim, ne olduğu konusunda kullanıcıya uygun çıktı eksikliği. Sözlüklerde tekrar eden tuşlara sahip olamayacağınıza göre, bazı tuşların kesiştiği iki sözlüğü birleştirmeyi nasıl halledersiniz? Elbette: "Umurumda değil" diyebilirsiniz, ancak bu yanlış döndürme / anahtarları tekrarlamak için bir istisna atma kuralını bozuyor.
Add
dışında birden fazla olabilir. Kesinlikle aynı ArgumentException
şeyi fırlatır Add
mı?
NamedElementException
??). ... birkaç farklı seçenek diyebilirim
Bunu yapabilirsin
Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);
public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
dic.Add(.., ..);
}
veya adres aralığı için bir Liste kullanın ve / veya yukarıdaki kalıbı kullanın.
List<KeyValuePair<string, string>>
MethodThatReturnAnotherDic
. OP'den geliyor. Lütfen soruyu ve cevabımı tekrar gözden geçirin.
Yeni bir Sözlükle uğraşıyorsanız (ve kaybedecek mevcut satırlarınız yoksa), her zaman başka bir nesne listesinden ToDictionary () kullanabilirsiniz.
Yani, senin durumunda şöyle bir şey yaparsın:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
Dictionary<string, string> dic = SomeList.ToDictionary...
Yinelenen anahtarlara sahip olmayacağınızı biliyorsanız şunları yapabilirsiniz:
dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Yinelenen bir anahtar / değer çifti varsa bir istisna atar.
Bunun neden çerçevede olmadığını bilmiyorum; olmalı. Belirsizlik yok; sadece bir istisna atın. Bu kod durumunda, bir istisna atar.
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
?
Şu şekilde bir uzatma yöntemi kullanmaktan çekinmeyin:
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;
}
Burada c # 7 ValueTuples (tuple değişmezleri) kullanan alternatif bir çözüm var
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);
}
}
Gibi kullanılmış
segments
.Zip(values, (s, v) => (s.AsSpan().StartsWith("{") ? s.Trim('{', '}') : null, v))
.Where(zip => zip.Item1 != null)
.AddTo(queryParams);
Başkalarının da belirttiği gibi, Dictionary<TKey,TVal>.AddRange
uygulanmamasının nedeni, yinelenen vakaları ele almak isteyebileceğiniz çeşitli yolların olmasıdır. Bu aynı zamanda böyledir Collection
ya arayüzleri gibi IDictionary<TKey,TVal>
, ICollection<T>
vb
Yalnızca List<T>
uygular IList<T>
ve aynı nedenlerden dolayı arabirimin olmadığını unutmayın : Bir koleksiyona bir dizi değer eklerken beklenen davranış, bağlama bağlı olarak geniş ölçüde değişebilir.
Sorunuzun bağlamı, kopyalar hakkında endişelenmediğinizi gösteriyor, bu durumda Linq kullanarak basit bir oneliner alternatifiniz var:
MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));