Estou retornando uma referência a um dicionário na minha propriedade somente leitura. Como evito que os consumidores alterem meus dados? Se fosse esse, IList
eu poderia simplesmente devolvê-lo AsReadOnly
. Existe algo semelhante que eu possa fazer com um dicionário?
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
fonte
fonte
Respostas:
Aqui está uma implementação simples que envolve um dicionário:
fonte
.NET 4.5
Introduz o .NET Framework 4.5 BCL
ReadOnlyDictionary<TKey, TValue>
( origem ).Como o BCL do .NET Framework 4.5 não inclui um
AsReadOnly
para dicionários, você precisará escrever seus próprios (se desejar). Seria algo como o seguinte, cuja simplicidade talvez destaca por que não era uma prioridade para o .NET 4.5..NET 4.0 e abaixo
Antes do .NET 4.5, não havia nenhuma classe de estrutura .NET que envolva um exemplo
Dictionary<TKey, TValue>
como o ReadOnlyCollection agrupa uma lista . No entanto, não é difícil criar um.Aqui está um exemplo - existem muitos outros se você pesquisar no Google no ReadOnlyDictionary .
fonte
AsReadOnly()
método habitualDictionary<,>
, então eu imagino quantas pessoas descobrirão seu novo tipo. Esse encadeamento de estouro de pilha ajudará, no entanto.Foi anunciado na recente conferência BUILD que desde o .NET 4.5, a interface
System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>
está incluída. A prova está aqui (Mono) e aqui (Microsoft);)Não tenho certeza se também
ReadOnlyDictionary
está incluído, mas pelo menos com a interface, não deve ser difícil criar agora uma implementação que exponha uma interface genérica oficial do .NET :)fonte
ReadOnlyDictionary<TKey, TValue>
(.Net 4.5) - msdn.microsoft.com/en-us/library/gg712875.aspxSinta-se livre para usar meu invólucro simples. Ele NÃO implementa o IDictionary, portanto, não precisa lançar exceções em tempo de execução para métodos de dicionário que alterariam o dicionário. Os métodos de mudança simplesmente não estão lá. Eu criei minha própria interface chamada IReadOnlyDictionary.
fonte
IDictionary
contrato. Eu acho que é mais correto do ponto de vista de OOP paraIDictionary
herdarIReadOnlyDictionary
.IDictionary
(para atualIReadOnlyDictionary
) eIMutableDictionary
(para atualIDictionary
).IsReadOnly on
IDictionary<TKey,TValue>
é herdado deICollection<T>
(IDictionary<TKey,TValue>
estendeICollection<T>
comoICollection<KeyValuePair<TKey,TValue>>
). Ele não é usado ou implementado de forma alguma (e de fato é "oculto" pelo uso explícito da implementação dosICollection<T>
membros associados ).Existem pelo menos três maneiras de solucionar o problema:
IDictionary<TKey, TValue>
e agrupe / delegue para um dicionário interno, conforme sugeridoICollection<KeyValuePair<TKey, TValue>>
conjunto como somente leitura ouIEnumerable<KeyValuePair<TKey, TValue>>
dependendo do uso do valor.ctor(IDictionary<TKey, TValue>)
e retorne uma cópia - dessa forma, o usuário estará livre para fazer o que quiser e não terá impacto no estado do objeto que hospeda o dicionário de origem. Observe que se o dicionário que você está clonando contiver tipos de referência (não cadeias de caracteres, como mostrado no exemplo), será necessário fazer a cópia "manualmente" e clonar os tipos de referência também.Como um aparte; ao expor coleções, tente expor a menor interface possível - no caso de exemplo, deve ser IDictionary, pois isso permite variar a implementação subjacente sem violar o contrato público que o tipo expõe.
fonte
Um dicionário somente leitura pode, até certo ponto, ser substituído por
Func<TKey, TValue>
- eu normalmente uso isso em uma API se eu quero apenas pessoas que realizam pesquisas; é simples e, em particular, é simples substituir o back-end, se você desejar. No entanto, não fornece a lista de chaves; se isso importa depende do que você está fazendo.fonte
Não, mas seria fácil criar o seu próprio. IDictionary define uma propriedade IsReadOnly. Basta quebrar um dicionário e lançar uma NotSupportedException dos métodos apropriados.
fonte
Nenhum disponível na BCL. No entanto, publiquei um ReadOnlyDictionary (chamado ImmutableMap) no meu projeto BCL Extras
Além de ser um dicionário totalmente imutável, ele suporta a produção de um objeto proxy que implementa o IDictionary e pode ser usado em qualquer lugar em que o IDictionary seja utilizado. Ele emitirá uma exceção sempre que uma das APIs mutantes for chamada
fonte
Você pode criar uma classe que implemente apenas uma implementação parcial do dicionário e oculte todas as funções de adicionar / remover / definir.
Use um dicionário internamente para o qual a classe externa passe todas as solicitações.
No entanto, como seu dicionário provavelmente contém tipos de referência, não há como impedir o usuário de definir valores nas classes mantidas pelo dicionário (a menos que essas classes sejam apenas de leitura)
fonte
Eu não acho que exista uma maneira fácil de fazer isso ... se o seu dicionário fizer parte de uma classe personalizada, você poderá obtê-lo com um indexador:
fonte
+1 Ótimo trabalho, Thomas. Levei o ReadOnlyDictionary um passo adiante.
Muito parecido com a solução da Dale, eu queria remover
Add()
,Clear()
,Remove()
, etc de IntelliSense. Mas eu queria que meus objetos derivados fossem implementadosIDictionary<TKey, TValue>
.Além disso, gostaria que o seguinte código fosse quebrado: (Novamente, a solução de Dale também faz isso)
A linha Add () resulta em:
O chamador ainda pode convertê-lo
IDictionary<TKey, TValue>
, mas o valorNotSupportedException
será aumentado se você tentar usar os membros não somente leitura (da solução de Thomas).Enfim, aqui está a minha solução para quem também queria isso:
fonte
Agora, existem coleções imutáveis da Microsoft (
System.Collections.Immutable
). Obtê-los via NuGet .fonte
fonte
public IEnumerable<KeyValuePair<string, string>> MyDictionary() { return _mydictionary; }
Esta é uma solução ruim, veja abaixo.
Para aqueles que ainda usam o .NET 4.0 ou anterior, tenho uma classe que funciona exatamente como a da resposta aceita, mas é muito mais curta. Ele estende o objeto Dicionário existente, substituindo (na verdade ocultando) determinados membros para que eles gerem uma exceção quando chamados.
Se o chamador tentar chamar Adicionar, Remover ou alguma outra operação de mutação que o Dicionário interno possui, o compilador lançará um erro. Eu uso os atributos obsoletos para gerar esses erros do compilador. Dessa maneira, você pode substituir um Dicionário por este ReadOnlyDictionary e ver imediatamente onde estão os problemas sem precisar executar o aplicativo e aguardar exceções em tempo de execução.
Dê uma olhada:
Esta solução tem um problema apontado por @supercat ilustrado aqui:
Em vez de fornecer um erro em tempo de compilação, como eu esperava, ou uma exceção de tempo de execução, como eu esperava, esse código é executado sem erros. Imprime quatro números. Isso faz do meu ReadOnlyDictionary um ReadWriteDictionary.
fonte
Dictionary<TKey,TValue>
sem queixas do compilador, e lançar ou coagir uma referência ao tipo de dicionário simples removerá as proteções.Dictionary
com umClone
método acorrentadoMemberwiseClone
. Infelizmente, embora seja possível clonar um dicionário de forma eficiente, clonando os repositórios, o fato de que os repositórios sãoprivate
mais do queprotected
significa que não há como uma classe derivada cloná-los; usarMemberwiseClone
sem também clonar as lojas de backup significa que as modificações subseqüentes feitas no dicionário original quebrarão o clone e as modificações feitas no clone quebrarão o original.