Comment obtenir un ReadOnlyCollection < T > des clés dans un dictionnaire < T, S >
-
08-07-2019 - |
Question
Ma classe contient un dictionnaire < T, S > dict
et je souhaite exposer un ReadOnlyCollection < T >
des clés. Comment faire cela sans copier le Dictionnaire < T, S > .KeyCollection dict.Keys
dans un tableau, puis exposer celui-ci en tant que ReadOnlyCollection
?
Je veux que ReadOnlyCollection
soit un wrapper approprié, c'est-à-dire. pour refléter les changements dans le dictionnaire sous-jacent, et comme je le comprends bien, copier la collection dans un tableau ne le fera pas (ainsi que sembler inefficace - je ne souhaite pas réellement une nouvelle collection, mais simplement pour exposer la collection sous-jacente de clés .. .) Toutes les idées seraient très appréciées!
Éditer: J'utilise C # 2.0, donc aucune méthode d'extension telle que .ToList (facilement) n'est disponible.
La solution
Si vous souhaitez vraiment utiliser ReadOnlyCollection < T > ;, le problème est que le constructeur de ReadOnlyCollection < T > prend un IList < T > ;, tandis que la KeyCollection du dictionnaire est uniquement un ICollection < T >.
Donc, si vous souhaitez encapsuler KeyCollection dans une ReadOnlyCollection, vous devez créer un type d'adaptateur (ou encapsuleur), implémentant IList < T > ;, encapsulant KeyCollection. Donc, cela ressemblerait à:
var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);
Pas très élégant cependant, d'autant plus que KeyCollection est déjà une collection en lecture seule et que vous pouvez simplement le transmettre sous forme de ICollection < T > :)
Autres conseils
DrJokepu a déclaré qu'il pourrait être difficile d'implémenter un wrapper pour Keys Collection. Mais, dans ce cas particulier, je pense que la mise en œuvre n’est pas si difficile car, comme nous le savons, il s’agit d’un wrapper en lecture seule.
Cela nous permet d'ignorer certaines méthodes qui, dans d'autres cas, seraient difficiles à implémenter.
Voici une implémentation rapide du wrapper pour Dictionary.KeyCollection:
class MyListWrapper<T, TValue> : IList<T>
{
private Dictionary<T, TValue>.KeyCollection keys;
public MyListWrapper(Dictionary<T, TValue>.KeyCollection keys)
{
this.keys = keys;
}
#region IList<T> Members
public int IndexOf(T item)
{
if (item == null)
throw new ArgumentNullException();
IEnumerator<T> e = keys.GetEnumerator();
int i = 0;
while (e.MoveNext())
{
if (e.Current.Equals(item))
return i;
i++;
}
throw new Exception("Item not found!");
}
public void Insert(int index, T item)
{
throw new NotImplementedException();
}
public void RemoveAt(int index)
{
throw new NotImplementedException();
}
public T this[int index]
{
get
{
IEnumerator<T> e = keys.GetEnumerator();
if (index < 0 || index > keys.Count)
throw new IndexOutOfRangeException();
int i = 0;
while (e.MoveNext() && i != index)
{
i++;
}
return e.Current;
}
set
{
throw new NotImplementedException();
}
}
#endregion
#region ICollection<T> Members
public void Add(T item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(T item)
{
return keys.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
keys.CopyTo(array, arrayIndex);
}
public int Count
{
get { return keys.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(T item)
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return keys.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return keys.GetEnumerator();
}
#endregion
}
Ce n'est peut-être pas la meilleure implémentation pour ces méthodes :) mais c'était simplement pour prouver que cela pouvait être fait.
En supposant que vous utilisez C # 3.0 et que vous avez:
Dictionnaire < T, S > d;
Alors
ReadOnlyCollection < T > r = new ReadOnlyCollection < T > (d.Keys.ToList ());
Vous devrez également importer l'espace de noms System.Linq.
Malheureusement, vous ne pouvez pas accéder à cette information autant que je sache sous le nom KeyCollection < T >
n'expose aucun élément qui vous permettrait de le faire facilement.
Vous pouvez toutefois placer la sous-classe ReadOnlyCollection < T >
de sorte que son constructeur reçoive le dictionnaire lui-même et remplace les méthodes appropriées de sorte qu'il expose les éléments du dictionnaire comme s'il s'agissait de ses propres éléments.
Pour mémoire, dans .NET 4.6, KeyCollection < T >
implémente IReadOnlyCollection < T >
. Si vous utilisez cette interface, vous pouvez toujours afficher les modifications apportées. Le dictionnaire contient toujours O (1) et comme l'interface est covariante, vous pouvez renvoyer IReadOnlyCollection < un type de base >
C'est moche, mais ça va le faire
Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));