Le titre est assez basique, pourquoi ne puis-je pas:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
c#
.net
dictionary
Custodio
la source
la source
Réponses:
Un commentaire à la question originale résume assez bien cela:
Quant à savoir pourquoi? Eh bien, probablement parce que le comportement de la fusion de dictionnaires ne peut pas être raisonné d'une manière qui correspond aux directives du Framework.
AddRange
n'existe pas car une plage n'a aucune signification pour un conteneur associatif, car la plage de données permet des entrées en double. Par exemple, si vous en aviez une,IEnumerable<KeyValuePair<K,T>>
cette collection ne vous protège pas contre les entrées en double.Le comportement consistant à ajouter une collection de paires clé-valeur, ou même à fusionner deux dictionnaires est simple. Le comportement de la façon de traiter plusieurs entrées en double, cependant, ne l'est pas.
Quel doit être le comportement de la méthode lorsqu'elle traite un doublon?
Il y a au moins trois solutions auxquelles je peux penser:
Lorsqu'une exception est levée, quel devrait être l'état du dictionnaire d'origine?
Add
est presque toujours implémentée comme une opération atomique: elle réussit et met à jour l'état de la collection, ou elle échoue, et l'état de la collection reste inchangé. Comme celaAddRange
peut échouer en raison d'erreurs dupliquées, la façon de garder son comportement cohérentAdd
serait de le rendre également atomique en lançant une exception sur tout doublon, et de laisser l'état du dictionnaire d'origine inchangé.En tant que consommateur d'API, il serait fastidieux de devoir supprimer de manière itérative les éléments en double, ce qui implique que le
AddRange
devrait lever une seule exception contenant toutes les valeurs en double.Le choix se résume alors à:
Il existe des arguments pour prendre en charge les deux cas d'utilisation. Pour ce faire, ajoutez-vous un
IgnoreDuplicates
drapeau à la signature?L'
IgnoreDuplicates
indicateur (lorsqu'il est défini sur true) fournirait également une accélération significative, car l'implémentation sous-jacente contournerait le code pour la vérification des doublons.Alors maintenant, vous avez un indicateur qui permet à la
AddRange
de prendre en charge les deux cas, mais a un effet secondaire non documenté (ce que les concepteurs du Framework ont travaillé très dur pour éviter).Résumé
Comme il n'y a pas de comportement clair, cohérent et attendu lorsqu'il s'agit de traiter les doublons, il est plus facile de ne pas les traiter tous ensemble et de ne pas fournir la méthode pour commencer.
Si vous devez continuellement fusionner des dictionnaires, vous pouvez bien sûr écrire votre propre méthode d'extension pour fusionner des dictionnaires, qui se comportera d'une manière qui fonctionne pour vos applications.
la source
AddMultiple
est différent deAddRange
, quelle que soit sa mise en œuvre va être bancale: lancez-vous une exception avec un tableau de toutes les clés en double? Ou lancez-vous une exception sur la première clé en double que vous rencontrez? Quel devrait être l'état du dictionnaire si une exception est levée? Pristine, ou toutes les clés qui ont réussi?Add
- soit envelopper chacunAdd
dans untry...catch
et attraper les doublons de cette façon; ou utilisez l'indexeur et remplacez la première valeur par la dernière valeur; ou vérifiez de manière préventive en utilisantContainsKey
avant d'essayerAdd
, préservant ainsi la valeur d'origine. Si le framework avait une méthodeAddRange
ouAddMultiple
, le seul moyen simple de communiquer ce qui s'était passé serait via une exception, et la gestion et la récupération impliquées ne seraient pas moins compliquées.J'ai une solution:
...
S'amuser.
la source
ToList()
, un dictionnaire est un fichierIEnumerable<KeyValuePair<TKey,TValue>
. En outre, les deuxième et troisième méthodes de méthode seront lancées si vous ajoutez une valeur de clé existante. Pas une bonne idée, tu cherchaisTryAdd
? Enfin, le deuxième peut être remplacé parWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
n'est pas une bonne solution alors j'ai changé le code. Vous pouvez utilisertry { mainDic.AddRange(addDic); } catch { do something }
si vous n'êtes pas sûr de la troisième méthode. La deuxième méthode fonctionne parfaitement.Dans le cas où quelqu'un rencontre cette question comme moi, il est possible d'atteindre "AddRange" en utilisant les méthodes d'extension IEnumerable:
L'astuce principale lors de la combinaison de dictionnaires consiste à gérer les clés en double. Dans le code ci-dessus, c'est la partie
.Select(grp => grp.First())
. Dans ce cas, il prend simplement le premier élément du groupe de doublons mais vous pouvez y implémenter une logique plus sophistiquée si nécessaire.la source
dict1
n'utilise pas le comparateur d'égalité par défaut?var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Ma conjecture est le manque de sortie appropriée à l'utilisateur quant à ce qui s'est passé. Comme vous ne pouvez pas avoir de clés répétitives dans un dictionnaire, comment géreriez-vous la fusion de deux dictionnaires où certaines touches se croisent? Bien sûr, vous pouvez dire: "Je m'en fous", mais cela enfreint la convention consistant à renvoyer false / à lancer une exception pour la répétition des clés.
la source
Add
, à part cela, cela peut se produire plus d'une fois. Il jetterait le mêmeArgumentException
quiAdd
fait, sûrement?NamedElementException
??), soit levée à la place de soit en tant que innerException d'une ArgumentException, qui spécifie l'élément nommé qui est en conflit ... quelques options différentes, je diraisTu pourrais faire ça
ou utilisez une liste pour ajouter une plage et / ou utiliser le modèle ci-dessus.
la source
MethodThatReturnAnotherDic
. Cela vient de l'OP. Veuillez revoir la question et ma réponse.Si vous traitez avec un nouveau dictionnaire (et que vous n'avez pas de lignes existantes à perdre), vous pouvez toujours utiliser ToDictionary () à partir d'une autre liste d'objets.
Donc, dans votre cas, vous feriez quelque chose comme ceci:
la source
Dictionary<string, string> dic = SomeList.ToDictionary...
Si vous savez que vous n'allez pas avoir de clés en double, vous pouvez faire:
Il lèvera une exception s'il y a une paire clé / valeur en double.
Je ne sais pas pourquoi ce n'est pas dans le cadre; devrait être. Il n'y a pas d'incertitude; jetez simplement une exception. Dans le cas de ce code, il lève une exception.
la source
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
?N'hésitez pas à utiliser une méthode d'extension comme celle-ci:
la source
Voici une solution alternative utilisant c # 7 ValueTuples (littéraux de tuple)
Utilisé comme
la source
Comme d'autres l'ont mentionné, la raison pour laquelle
Dictionary<TKey,TVal>.AddRange
n'est pas implémenté est qu'il existe plusieurs façons de gérer les cas où vous avez des doublons. Ceci est également le cas pourCollection
ou interfaces telles queIDictionary<TKey,TVal>
,ICollection<T>
, etc.Seulement
List<T>
met en œuvre, et vous remarquerez que l'IList<T>
interface ne pas, pour les mêmes raisons: l' attendu comportement lors de l'ajout d'une plage de valeurs à une collection peut varier largement, selon le contexte.Le contexte de votre question suggère que vous ne vous inquiétez pas des doublons, auquel cas vous avez une simple alternative oneliner, en utilisant Linq:
la source