Je renvoie une référence à un dictionnaire dans ma propriété en lecture seule. Comment empêcher les consommateurs de modifier mes données? Si c'était un, IList
je pourrais simplement le retourner AsReadOnly
. Puis-je faire quelque chose de similaire avec un dictionnaire?
Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
Get
Return _mydictionary
End Get
End Property
.net
dictionary
readonly
Rob Sobers
la source
la source
Réponses:
Voici une implémentation simple qui encapsule un dictionnaire:
la source
.NET 4.5
Le .NET Framework 4.5 BCL introduit
ReadOnlyDictionary<TKey, TValue>
( source ).Comme le .NET Framework 4.5 BCL n'inclut pas
AsReadOnly
de dictionnaires for, vous devrez écrire le vôtre (si vous le souhaitez). Ce serait quelque chose comme ce qui suit, dont la simplicité souligne peut-être pourquoi ce n'était pas une priorité pour .NET 4.5..NET 4.0 et inférieur
Avant .NET 4.5, il n'y a pas de classe de framework .NET qui encapsule un
Dictionary<TKey, TValue>
comme ReadOnlyCollection encapsule un List . Cependant, il n'est pas difficile d'en créer un.Voici un exemple - il y en a beaucoup d'autres si vous recherchez ReadOnlyDictionary sur Google .
la source
AsReadOnly()
méthode sur l'habituelDictionary<,>
, alors je me demande combien de personnes découvriront leur nouveau type. Ce fil de discussion Stack Overflow aidera, cependant.Il a été annoncé lors de la récente conférence BUILD que depuis .NET 4.5, l'interface
System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>
est incluse. La preuve est ici (Mono) et ici (Microsoft);)Je ne sais pas si
ReadOnlyDictionary
est également inclus, mais au moins avec l'interface, il ne devrait pas être difficile de créer maintenant une implémentation qui expose une interface générique officielle .NET :)la source
ReadOnlyDictionary<TKey, TValue>
(.Net 4.5) - msdn.microsoft.com/en-us/library/gg712875.aspxN'hésitez pas à utiliser mon emballage simple. Il n'implémente PAS IDictionary, il n'a donc pas à lever d'exceptions lors de l'exécution pour les méthodes de dictionnaire qui modifieraient le dictionnaire. Les méthodes de changement ne sont tout simplement pas là. J'ai créé ma propre interface appelée IReadOnlyDictionary.
la source
IDictionary
contrat. Je pense que c'est plus correct du point de vue de la POO pourIDictionary
hériterIReadOnlyDictionary
.IDictionary
(pour le courantIReadOnlyDictionary
) etIMutableDictionary
(pour le courantIDictionary
).IsReadOnly on
IDictionary<TKey,TValue>
est hérité deICollection<T>
(IDictionary<TKey,TValue>
s'étend enICollection<T>
tant queICollection<KeyValuePair<TKey,TValue>>
). Il n'est en aucun cas utilisé ou implémenté (et est en fait "caché" par l'utilisation de l'implémentation explicite desICollection<T>
membres ).Il y a au moins 3 façons dont je peux voir pour résoudre le problème:
IDictionary<TKey, TValue>
et envelopper / déléguer à un dictionnaire interne comme cela a été suggéréICollection<KeyValuePair<TKey, TValue>>
ensemble en lecture seule ou enIEnumerable<KeyValuePair<TKey, TValue>>
fonction de l'utilisation de la valeur.ctor(IDictionary<TKey, TValue>)
et renvoyez une copie - de cette façon, l'utilisateur est libre d'en faire ce qu'il veut et cela n'a pas d'impact sur l'état de l'objet hébergeant le dictionnaire source. Notez que si le dictionnaire que vous clonez contient des types de référence (pas des chaînes comme indiqué dans l'exemple), vous devrez faire la copie "manuellement" et cloner également les types de référence.En aparté; lors de l'exposition de collections, essayez d'exposer la plus petite interface possible - dans le cas d'exemple, il devrait s'agir de IDictionary car cela vous permet de faire varier l'implémentation sous-jacente sans rompre le contrat public que le type expose.
la source
Un dictionnaire en lecture seule peut dans une certaine mesure être remplacé par
Func<TKey, TValue>
- J'utilise généralement ceci dans une API si je veux seulement que les gens effectuent des recherches; c'est simple, et en particulier, il est simple de remplacer le backend si jamais vous le souhaitez. Cependant, il ne fournit pas la liste des clés; si cela compte dépend de ce que vous faites.la source
Non, mais ce serait facile de rouler le vôtre. IDictionary définit une propriété IsReadOnly. Enveloppez simplement un dictionnaire et lancez une NotSupportedException à partir des méthodes appropriées.
la source
Aucun disponible dans la BCL. Cependant, j'ai publié un ReadOnlyDictionary (nommé ImmutableMap) dans mon projet BCL Extras
En plus d'être un dictionnaire totalement immuable, il prend en charge la production d'un objet proxy qui implémente IDictionary et peut être utilisé dans n'importe quel endroit où IDictionary est pris. Il lèvera une exception chaque fois qu'une des API en mutation est appelée
la source
Vous pouvez créer une classe qui n'implémente qu'une implémentation partielle du dictionnaire et masque toutes les fonctions d'ajout / suppression / ensemble.
Utilisez un dictionnaire en interne auquel la classe externe transmet toutes les requêtes.
Cependant, comme votre dictionnaire contient probablement des types de référence, il n'y a aucun moyen d'empêcher l'utilisateur de définir des valeurs sur les classes détenues par le dictionnaire (à moins que ces classes elles-mêmes ne soient en lecture seule)
la source
Je ne pense pas qu'il y ait un moyen facile de le faire ... si votre dictionnaire fait partie d'une classe personnalisée, vous pouvez y parvenir avec un indexeur:
la source
+1 Excellent travail, Thomas. J'ai poussé ReadOnlyDictionary un peu plus loin.
Tout comme la solution de Dale, je voulais enlever
Add()
,Clear()
,Remove()
, etc de IntelliSense. Mais je voulais que mes objets dérivés soient implémentésIDictionary<TKey, TValue>
.De plus, je voudrais que le code suivant se brise: (Encore une fois, la solution de Dale le fait aussi)
La ligne Add () donne:
L'appelant peut toujours le convertir
IDictionary<TKey, TValue>
, mais leNotSupportedException
sera déclenché si vous essayez d'utiliser les membres non en lecture seule (à partir de la solution de Thomas).Quoi qu'il en soit, voici ma solution pour tous ceux qui voulaient aussi ceci:
la source
Maintenant, il existe des collections immuables Microsoft (
System.Collections.Immutable
). Obtenez-les via NuGet .la source
la source
public IEnumerable<KeyValuePair<string, string>> MyDictionary() { return _mydictionary; }
C'est une mauvaise solution, voir en bas.
Pour ceux qui utilisent encore .NET 4.0 ou une version antérieure, j'ai une classe qui fonctionne exactement comme celle de la réponse acceptée, mais elle est beaucoup plus courte. Il étend l'objet Dictionary existant, remplaçant (masquant en fait) certains membres pour qu'ils lèvent une exception lorsqu'ils sont appelés.
Si l'appelant essaie d'appeler Add, Remove ou une autre opération de mutation du dictionnaire intégré, le compilateur lèvera une erreur. J'utilise les attributs obsolètes pour signaler ces erreurs de compilation. De cette façon, vous pouvez remplacer un dictionnaire par ce ReadOnlyDictionary et voir immédiatement où peuvent se trouver les problèmes sans avoir à exécuter votre application et à attendre les exceptions d'exécution.
Regarde:
Cette solution a un problème signalé par @supercat illustré ici:
Plutôt que de donner une erreur de compilation comme je m'y attendais, ou une exception d'exécution comme je l'espérais, ce code s'exécute sans erreur. Il imprime quatre nombres. Cela fait de mon ReadOnlyDictionary un ReadWriteDictionary.
la source
Dictionary<TKey,TValue>
sans aucune plainte du compilateur, et le cast ou la contrainte d'une référence au type de dictionnaire simple supprimera toutes les protections.Dictionary
avec uneClone
méthode enchaînéeMemberwiseClone
. Malheureusement, alors qu'il devrait être possible de cloner efficacement un dictionnaire en clonant les magasins de sauvegarde, le fait que les magasins de sauvegarde soientprivate
plutôt queprotected
signifie qu'il n'y a aucun moyen pour une classe dérivée de les cloner; l'utilisationMemberwiseClone
sans cloner également les magasins de sauvegarde signifiera que les modifications ultérieures apportées au dictionnaire original casseront le clone, et les modifications apportées au clone casseront l'original.