Der Titel ist einfach genug, warum kann ich nicht:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Der Titel ist einfach genug, warum kann ich nicht:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
Antworten:
Ein Kommentar zur ursprünglichen Frage fasst dies ziemlich gut zusammen:
weil niemand diese Funktion jemals entworfen, spezifiziert, implementiert, getestet, dokumentiert und ausgeliefert hat. - @Gabe Moothart
Warum? Nun, wahrscheinlich, weil das Verhalten beim Zusammenführen von Wörterbüchern nicht in einer Weise begründet werden kann, die den Framework-Richtlinien entspricht.
AddRangeexistiert nicht, weil ein Bereich für einen assoziativen Container keine Bedeutung hat, da der Datenbereich doppelte Einträge zulässt. Wenn Sie beispielsweise eine haben IEnumerable<KeyValuePair<K,T>>, schützt diese Sammlung nicht vor doppelten Einträgen.
Das Hinzufügen einer Sammlung von Schlüssel-Wert-Paaren oder sogar das Zusammenführen von zwei Wörterbüchern ist unkompliziert. Das Verhalten beim Umgang mit mehreren doppelten Einträgen ist jedoch nicht so.
Wie sollte sich die Methode verhalten, wenn es sich um ein Duplikat handelt?
Ich kann mir mindestens drei Lösungen vorstellen:
Wie sollte der Status des ursprünglichen Wörterbuchs sein, wenn eine Ausnahme ausgelöst wird?
Addwird fast immer als atomare Operation implementiert: Sie ist erfolgreich und aktualisiert den Status der Sammlung, oder sie schlägt fehl, und der Status der Sammlung bleibt unverändert. Da AddRangedies aufgrund doppelter Fehler fehlschlagen kann, besteht die Möglichkeit, das Verhalten konsistent zu halten Add, darin, es atomar zu machen, indem eine Ausnahme für ein Duplikat ausgelöst wird, und den Status des ursprünglichen Wörterbuchs unverändert zu lassen.
Als API-Konsument wäre es mühsam, doppelte Elemente iterativ entfernen zu müssen, was bedeutet, dass AddRangeeine einzige Ausnahme ausgelöst werden sollte, die alle doppelten Werte enthält.
Die Wahl läuft dann auf Folgendes hinaus:
Es gibt Argumente für die Unterstützung beider Anwendungsfälle. Fügen Sie IgnoreDuplicatesdazu der Signatur ein Flag hinzu?
Das IgnoreDuplicatesFlag (wenn es auf true gesetzt ist) würde ebenfalls eine erhebliche Beschleunigung bewirken, da die zugrunde liegende Implementierung den Code für die doppelte Überprüfung umgehen würde.
Jetzt haben Sie ein Flag, mit dem AddRangebeide Fälle unterstützt werden können, das jedoch einen undokumentierten Nebeneffekt hat (was die Framework-Designer sehr hart vermieden haben).
Zusammenfassung
Da es beim Umgang mit Duplikaten kein klares, konsistentes und erwartetes Verhalten gibt, ist es einfacher, nicht alle zusammen zu behandeln und zunächst nicht die Methode bereitzustellen.
Wenn Sie ständig Wörterbücher zusammenführen müssen, können Sie natürlich Ihre eigene Erweiterungsmethode zum Zusammenführen von Wörterbüchern schreiben, die sich so verhält, wie es für Ihre Anwendung (en) funktioniert.
AddMultipleist anders als AddRange, unabhängig davon, ob die Implementierung wackelig sein wird: Wirfst du eine Ausnahme mit einem Array aller doppelten Schlüssel? Oder werfen Sie eine Ausnahme auf den ersten doppelten Schlüssel, auf den Sie stoßen? Wie sollte der Status des Wörterbuchs sein, wenn eine Ausnahme ausgelöst wird? Makellos oder alle Schlüssel, die erfolgreich waren?
Add- wickeln Sie entweder jede Addin a ein try...catchund fangen Sie die Duplikate auf diese Weise ab. oder verwenden Sie den Indexer und überschreiben Sie den ersten Wert mit dem späteren Wert; oder überprüfen Sie die Verwendung vorab, ContainsKeybevor Sie dies versuchen Add, und behalten Sie so den ursprünglichen Wert bei. Wenn das Framework eine AddRangeoder AddMultiple-Methode hätte, wäre die einzige einfache Möglichkeit, das Geschehene zu kommunizieren, eine Ausnahme, und die damit verbundene Handhabung und Wiederherstellung wäre nicht weniger kompliziert.
Ich habe eine Lösung:
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;
}
}
}
}
Habe Spaß.
ToList(), ein Wörterbuch ist ein IEnumerable<KeyValuePair<TKey,TValue>. Die zweite und dritte Methodenmethode werden auch ausgelöst, wenn Sie einen vorhandenen Schlüsselwert hinzufügen. Keine gute Idee, suchten Sie TryAdd? Schließlich kann die zweite durchWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()ist keine gute Lösung, also habe ich den Code geändert. Sie können verwenden, try { mainDic.AddRange(addDic); } catch { do something }wenn Sie sich für die dritte Methode nicht sicher sind. Die zweite Methode funktioniert perfekt.
Falls jemand wie ich auf diese Frage stößt, ist es möglich, "AddRange" mithilfe von IEnumerable-Erweiterungsmethoden zu erreichen:
var combined =
dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Der Haupttrick beim Kombinieren von Wörterbüchern ist der Umgang mit den doppelten Schlüsseln. Im obigen Code ist es der Teil .Select(grp => grp.First()). In diesem Fall wird einfach das erste Element aus der Gruppe der Duplikate verwendet, aber Sie können dort bei Bedarf eine komplexere Logik implementieren.
dict1 nicht verwendet wird?
var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Ich vermute, dass dem Benutzer keine ordnungsgemäße Ausgabe darüber vorliegt, was passiert ist. Wie würden Sie mit dem Zusammenführen von zwei Wörterbüchern umgehen, bei denen sich einige Schlüssel überschneiden, da sich Schlüssel in einem Wörterbuch nicht wiederholen können? Sicher könnte man sagen: "Es ist mir egal", aber das verstößt gegen die Konvention, falsch zurückzugeben / eine Ausnahme für das Wiederholen von Schlüsseln auszulösen.
AddAbgesehen davon kann dies mehr als einmal vorkommen. Es würde die gleiche werfen ArgumentExceptiondas der AddFall ist, oder ?
NamedElementException??), der entweder anstelle oder als innerException einer ArgumentException ausgelöst wird und das benannte Element angibt, das in Konflikt steht ... ein paar verschiedene Optionen, würde ich sagen
Du könntest das tun
Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);
public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
dic.Add(.., ..);
}
oder verwenden Sie eine Liste für die Adressierung und / oder verwenden Sie das obige Muster.
List<KeyValuePair<string, string>>
MethodThatReturnAnotherDic. Es kommt aus dem OP. Bitte überprüfen Sie die Frage und meine Antwort erneut.
Wenn Sie mit einem neuen Wörterbuch arbeiten (und keine vorhandenen Zeilen zu verlieren haben), können Sie ToDictionary () immer aus einer anderen Liste von Objekten verwenden.
In Ihrem Fall würden Sie also Folgendes tun:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);
Dictionary<string, string> dic = SomeList.ToDictionary...
Wenn Sie wissen, dass Sie keine doppelten Schlüssel haben werden, können Sie Folgendes tun:
dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Es wird eine Ausnahme ausgelöst, wenn ein doppeltes Schlüssel / Wert-Paar vorhanden ist.
Ich weiß nicht, warum dies nicht im Rahmen ist; sollte sein. Es gibt keine Unsicherheit; Wirf einfach eine Ausnahme. Bei diesem Code wird eine Ausnahme ausgelöst.
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);?
Fühlen Sie sich frei, Erweiterungsmethode wie folgt zu verwenden:
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;
}
Hier ist eine alternative Lösung mit c # 7 ValueTuples (Tupelliterale)
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);
}
}
Gebraucht wie
segments
.Zip(values, (s, v) => (s.AsSpan().StartsWith("{") ? s.Trim('{', '}') : null, v))
.Where(zip => zip.Item1 != null)
.AddTo(queryParams);
Wie bereits erwähnt, liegt der Grund für die Nichtimplementierung darin, dass Dictionary<TKey,TVal>.AddRangeSie auf verschiedene Weise mit Fällen umgehen können, in denen Duplikate vorhanden sind. Dies ist auch der Fall für Collectionoder Schnittstellen wie IDictionary<TKey,TVal>, ICollection<T>usw.
Nur List<T>implementiert, und Sie werden feststellen , dass die IList<T>Schnittstelle nicht, aus den gleichen Gründen: das erwartete Verhalten , wenn eine Reihe von Werten zu einer Sammlung hinzugefügt breit variieren kann, je nach Kontext.
Der Kontext Ihrer Frage legt nahe, dass Sie sich keine Sorgen um Duplikate machen. In diesem Fall haben Sie eine einfache Oneliner-Alternative mit Linq:
MethodThatReturnAnotherDic().ToList.ForEach(kvp => dic.Add(kvp.Key, kvp.Value));