Comment puis-je créer une nouvelle instance de ImmutableDictionary?
Je voudrais écrire quelque chose comme ceci:
var d = new ImmutableDictionary<string, int> { { "a", 1 }, { "b", 2 } };
(à l'aide de ImmutableDictionary
de Système.Les Collections.Immuable). Il semble comme un simple d'utilisation que je suis de déclarer toutes les valeurs d'avance -- aucune mutation n'. Mais cela me donne l'erreur:
Type "
System.Collections.Immutable.ImmutableDictionary<TKey,TValue>
" n'a pas de constructeurs définis
Comment je suis censé créer un nouveau immuable dictionnaire avec un contenu statique?
Vous devez vous connecter pour publier un commentaire.
Vous ne pouvez pas créer immuable collection avec un initialiseur de collection parce que le compilateur traduit en une séquence d'appels à la
Add
méthode. Par exemple, si vous regardez le code IL pourvar d = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
vous aurezÉvidemment, cela viole la notion de immuable collections.
À la fois votre propre réponse et Jon Skeet sont des façons de composer avec cette.
De créer un "normal" dictionnaire d'abord et appeler
ToImmutableDictionary
(selon votre propre réponse), ou de l'utilisationImmutableDictionary<,>.Builder
:C'est dommage que le constructeur n'a pas un constructeur public autant que je peux dire, car il vous empêche d'utiliser la syntaxe d'initialiseur de collection, à moins que j'ai raté quelque chose... le fait que le
Add
méthode renvoievoid
signifie que vous ne pouvez même pas de la chaîne des appels vers lui, le rendant plus ennuyeux - aussi loin que je peux voir, vous ne pouvez pas utiliser un générateur pour créer un immuable dictionnaire en une seule expression, ce qui est très frustrant 🙁Add
d'être annulé. Ick! Voir l'horrible modifier...AddRange
méthode de paires clé-valeur, ce qui est assez proche du constructeur pour elle, mais je pense qu'à ce stade, il serait plus simple d'utiliser les méthodes d'extension qui ont également été publiés avec la bibliothèque.Builder
type semble presque inutile à l'heure actuelle, de l'OMI.ToImmutableDictionary
approche semble être l'un à éviter, parce qu'il y a surcharge considérable dans la construction d'unDictionary<TKey, TValue>
- il crée une table de hachage en interne pour favoriser l'efficacité de la recherche. Mais aussi loin que je peux dire,ToImmutableDictionary
juste itère sur le contenu, de sorte que tout le travail pour construire la table de hachage est tout simplement perdu.ImmutableDictionary.Builder.Add("aKey","aValue").Add("anotherKey","anotherValue").Build()
comme tous les autres builder jamais écrit.Vous pouvez utiliser une aide comme ceci:
(Je suis en utilisant le C# 6 l'expression des corps des membres, donc si vous voulez le compiler sur les anciennes versions, vous aurez besoin de développer de ceux que les membres à part entière.)
Avec ce type, vous pouvez utiliser la syntaxe d'initialiseur de collection comme:
ou si vous êtes à l'aide de C# 6 vous pouvez utiliser initialiseur d'objet de syntaxe, avec son nouveau support pour les indexeurs (c'est pourquoi j'ai inclus une écriture seule indexeur dans mon genre):
Cette combine les avantages des deux proposées avantages:
Dictionary<TKey, TValue>
Le problème de l'élaboration d'un
Dictionary<TKey, TValue>
est qu'il y a un tas de frais généraux impliqués dans la construction de cette façon; c'est inutilement coûteux de passer de ce qui est en fait une liste de paires clé/valeur, parce qu'il aura soigneusement mis en place une table de hachage de la structure pour permettre des recherches que vous n'aurez jamais l'utiliser effectivement. (L'objet que vous devez effectuer des recherches sur le immuable du dictionnaire finalement vous retrouver avec, pas la mutable dictionnaire que vous utilisez lors de l'initialisation.)ToImmutableDictionary
est de simplement aller à parcourir le contenu du dictionnaire (un processus rendu moins efficace par la routeDictionary<TKey, TValue>
fonctionne en interne, - il faut plus de travail pour faire ce qu'avec une simple liste), l'obtention d'absolument aucun avantage à partir du travail qui est entré dans la construction du dictionnaire, et ensuite faire le même travail, il l'aurait fait si vous aviez utilisé le constructeur directement.De Jon code permet d'éviter cela en utilisant uniquement le constructeur, qui doit être plus efficace. Mais son approche ne permet pas d'utiliser les initialiseurs.
Je partage Jon frustration que l'immuable collections ne fournissent pas un moyen de le faire sortir de la boîte.
Édité 2017/08/10: j'ai dû changer le zéro argument constructeur qui prend un argument qu'il ignore, et de passer à un mannequin de la valeur partout où vous utilisez ce. @gareth-latty a souligné dans un commentaire qu'un
struct
ne peut pas avoir un zéro-les arguments du constructeur. Quand j'ai d'abord écrit cet exemple qui n'était pas vrai: pour un temps, des aperçus de C# 6 vous a permis de fournir un tel constructeur. Cette fonctionnalité a été supprimée avant de C# 6 expédié (après, j'ai écrit la réponse originale à cette question, évidemment), probablement parce que c'était confus, il y avait des scénarios dans lesquels le constructeur ne pas s'exécuter. Dans ce cas particulier, il était sûr de l'utiliser, mais malheureusement la fonctionnalité langue n'existe plus. Par Gareth a été suggéré de modifier dans une classe, mais alors n'importe quel code à l'aide de ce devrait allouer un objet, causant inutiles GC pression - la seule raison pour laquelle j'ai utilisé unstruct
était de rendre possible l'utilisation de cette syntaxe avec aucune autre gestion d'exécution.J'ai essayé de modifier cette option pour effectuer reportés de l'initialisation de l'
_builder
mais il s'avère que le JIT générateur de code n'est pas assez intelligent pour optimiser ces loin, de sorte que même dans les versions release, il vérifie_builder
pour chaque élément que vous ajoutez. (Et il inlines que l'enregistrement et l'appel correspondant àCreateBuilder
qui s'avère produire beaucoup de code avec beaucoup de branchements conditionnels). C'est vraiment mieux d'avoir un temps d'initialisation, et cela doit se produire dans le constructeur, si vous voulez être en mesure d'utiliser cette syntaxe d'initialiseur. Donc le seul moyen d'utiliser cette syntaxe avec aucun coût supplémentaire est d'avoir une structure qui initialise_builder
dans son constructeur, ce qui signifie que nous avons maintenant besoin de ce vilain argument.struct
, de sorte que vous auriez à en faire uneclass
.struct
dans l'aperçu de C# 6 qui a été disponible lorsque l'origine, j'avais écrit ça. À l'aide d'unclass
ajoute une allocation supplémentaire, ce qui signifie que ce est plus que juste du sucre syntaxique serait - il alors imposer une gestion d'exécution. C'est pourquoi j'ai fait unstruct
. J'ai donc fait un peu maladroit de mise à jour qui permet à ce continuer à travailler efficacement, mais avec le peu malheureux besoin d'ajouter un inutile ctor arg. (Le compilateur JIT optimise l'argument de l'existence dans les versions release, en conservant l'exécution de l'efficacité de l'original).Jusqu'à présent j'aime le plus:
Ou ce
Il est également AddRange méthode disponible.