Pergunta

Tenho um objeto em um ambiente multithread que mantém uma coleção de informações, por exemplo:

public IList<string> Data 
{
    get 
    {
        return data;
    }
}

Atualmente tenho return data; envolto por um ReaderWriterLockSlim para proteger a coleção contra violações de compartilhamento.Porém, para ter certeza, gostaria de retornar a coleção como somente leitura, para que o código de chamada não consiga fazer alterações na coleção, apenas visualize o que já está lá.Tudo isso é possível?

Foi útil?

Solução

Se sua única intenção é chamar o código para não cometer erros e modificar a coleção quando ela deveria estar apenas lendo tudo o que é necessário é retornar uma interface que não suporta Adicionar, Remover, etc.Por que não voltar IEnumerable<string>?O código de chamada teria que ser convertido, o que é improvável que eles façam sem conhecer os detalhes internos da propriedade que estão acessando.

Se, no entanto, sua intenção é evitar que o código de chamada observe atualizações de outros threads, você terá que recorrer às soluções já mencionadas, para realizar uma cópia profunda ou superficial, dependendo da sua necessidade.

Outras dicas

Se seus dados subjacentes forem armazenados como uma lista, você poderá usar Lista(T).AsReadOnly método.
Se seus dados puderem ser enumerados, você poderá usar Enumerable.ToList método para converter sua coleção em List e chamar AsReadOnly nela.

Votei na sua resposta aceita e concordo com ela - mas posso lhe dar algo a considerar?

Não devolva uma coleção diretamente.Crie uma classe de lógica de negócios nomeada com precisão que reflita a finalidade da coleção.

A principal vantagem disso está no fato de que você não pode adicionar código às coleções, portanto, sempre que tiver uma "coleção" nativa em seu modelo de objeto, você SEMPRE terá código de suporte não-OO espalhado por todo o seu projeto para acessá-lo.

Por exemplo, se sua coleção fosse faturas, você provavelmente teria 3 ou 4 locais em seu código onde iterou faturas não pagas.Você poderia ter um método getUnpaidInvoices.No entanto, o verdadeiro poder surge quando você começa a pensar em métodos como "payUnpaidInvoices(payer, account);".

Quando você passa coleções em vez de escrever um modelo de objeto, classes inteiras de refatorações nunca ocorrerão a você.

Observe também que isso torna o seu problema particularmente agradável.Se você não quiser que as pessoas alterem as coleções, seu contêiner não precisará conter modificadores.Se você decidir mais tarde que em apenas um caso você realmente PRECISA modificá-lo, você pode criar um mecanismo seguro para fazer isso.

Como você resolve esse problema ao distribuir uma coleção nativa?

Além disso, as coleções nativas não podem ser aprimoradas com dados extras.Você reconhecerá isso na próxima vez que passar (Coleção, Extra) para mais de um ou dois métodos.Indica que "Extra" pertence ao objeto que contém sua coleção.

Acho que você está confundindo conceitos aqui.

O ReadOnlyCollection fornece um wrapper somente leitura para uma coleção existente, permitindo que você (Classe A) passe uma referência à coleção com segurança, sabendo que o chamador (Classe B) não pode modificar a coleção (ou seja,não pode adicionar ou remover quaisquer elementos da coleção.)

Não há absolutamente nenhuma garantia de segurança de thread.

  • Se você (Classe A) continuar a modificar a coleção subjacente depois de distribuí-la como um ReadOnlyCollection então a classe B verá essas alterações, terá quaisquer iteradores invalidados, etc.e geralmente estar aberto a qualquer um dos problemas usuais de simultaneidade com coleções.
  • Além disso, se os elementos da coleção forem mutáveis, você (Classe A) e o chamador (Classe B) poderá alterar qualquer estado mutável dos objetos da coleção.

Sua implementação depende de suas necessidades:- Se você não se importa com o fato de o chamador (Classe B) ver quaisquer alterações na coleção, basta clonar a coleção, distribuí-la e parar de se importar.- Se você definitivamente precisa que o chamador (Classe B) veja as alterações feitas na coleção e deseja que isso seja seguro para threads, então você tem mais problemas em suas mãos.Uma possibilidade é implementar sua própria variante thread-safe do ReadOnlyCollection para permitir acesso bloqueado, embora isso não seja trivial e não tenha desempenho se você quiser oferecer suporte a IEnumerable, e ainda não irá protegê-lo contra elementos mutáveis ​​na coleção.

Deve-se notar que akua resposta de apenas protegerá a lista como somente leitura.Os elementos da lista ainda são muito graváveis.Não sei se existe alguma maneira de proteger elementos não atômicos sem cloná-los antes de colocá-los na lista somente leitura.

Você quer usar o colheita palavra-chave.Você percorre a lista IEnumerable e retorna os resultados com rendimento.Isso permite que o consumidor use for each sem modificar a coleção.

Seria algo assim:

List<string> _Data;
public IEnumerable<string> Data
{
  get
  {
    foreach(string item in _Data)
    {
      return yield item;
    }
  }
}

Você pode usar uma cópia da coleção.

public IList<string> Data {
get {
    return new List<T>(data);
}}

Dessa forma, não importa se ele for atualizado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top