Como obter um ReadOnlyCollection das chaves em um dicionário
-
08-07-2019 - |
Pergunta
Meu classe contém um Dictionary<T, S> dict
, e eu quero expor um ReadOnlyCollection<T>
das chaves. Como posso fazer isso sem copiar o Dictionary<T, S>.KeyCollection dict.Keys
para uma matriz e, em seguida, expondo a matriz como uma ReadOnlyCollection
?
Eu quero o ReadOnlyCollection
ser um invólucro próprio, ie. para refletir as mudanças no dicionário subjacente, e como eu o entendo copiar a coleção para uma matriz não vai fazer isso (assim como parecendo ineficiente - Eu realmente não quero uma nova coleção, apenas para expor a coleção subjacente de chaves .. .). Alguma idéia seria muito apreciada!
Edit: eu estou usando C # 2.0, portanto, não têm métodos de extensão, como .ToList (facilmente) disponíveis.
Solução
Se você realmente quiser usar ReadOnlyCollection
Então, se você quer envolver o KeyCollection em um ReadOnlyCollection, você terá que criar um tipo de adaptador (ou invólucro), a implementação de IList
var dictionary = ...;
var readonly_keys = new ReadOnlyCollection<T> (new CollectionListWrapper<T> (dictionary.Keys)
);
Não é muito elegante, porém, especialmente como o KeyCollection é já uma coleção somente leitura, e que você poderia simplesmente passá-lo em torno de como um ICollection
Outras dicas
DrJokepu disse que poderia ser difícil de implementar um wrapper para Chaves Collection. Mas, neste caso particular, eu acho que a implementação não é tão difícil, porque, como sabemos, este é um wrapper somente leitura.
Isso nos permite ignorar alguns métodos que, em outro caso, seria difícil de implementar.
Aqui está uma rápida implementação do wrapper para 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
}
Isto pode não ser a melhor implementação para esses métodos :) mas foi apenas para provar que isso pode ser feito.
Supondo que você estiver usando C # 3.0 e você tem:
dicionário
Em seguida
ReadOnlyCollection
Você também vai precisar para importar o namespace System.Linq.
Infelizmente não é possível para que direcly, tanto quanto eu sei como KeyCollection<T>
não expõe qualquer coisa que lhe permitiria fazer isso facilmente.
Você pode, no entanto, subclasse ReadOnlyCollection<T>
para que seu construtor recebe o dicionário próprio e substituir os métodos adequados para que ele expõe os itens do dicionário como se fossem seus próprios itens.
Para o registro, em .NET 4.6, o KeyCollection<T>
implementos IReadOnlyCollection<T>
, por isso, se você usar essa interface, você ainda pode refletir mudanças no dicionário, ainda obter O (1) contém, e porque a interface é covariant, você pode retorno IReadOnlyCollection<some base type>
É feio, mas isso vai fazê-lo
Dictionary<int,string> dict = new Dictionary<int, string>();
...
ReadOnlyCollection<int> roc = new ReadOnlyCollection<int>((new List<int>((IEnumerable<int>)dict.Keys)));