Pergunta

É possível marcar de alguma forma um System.Array como imutável.Quando colocados atrás de um conjunto público/privado, eles não podem ser adicionados, pois requer realocação e reatribuição, mas um consumidor ainda pode definir qualquer subscrito que desejar:

public class Immy
{
    public string[] { get; private set; }
}

Eu pensei que o readonly palavra-chave pode resolver o problema, mas não tive essa sorte.

Foi útil?

Solução

ReadOnlyCollection<T> é provavelmente o que você está procurando. Não tem um Add() método.

Outras dicas

o Diretrizes de design da estrutura sugerir devolver uma cópia da matriz. Dessa forma, os consumidores não podem alterar os itens da matriz.

// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealed class Path {
   public static readonly char[] InvalidPathChars = 
      { '\"', '<', '>', '|' };
}

Estes são melhores:

public static ReadOnlyCollection<char> GetInvalidPathChars(){
   return Array.AsReadOnly(InvalidPathChars);
}

public static char[] GetInvalidPathChars(){
   return (char[])InvalidPathChars.Clone();
}

Os exemplos são diretamente do livro.

Por favor, veja Coleções imutáveis ​​agora disponíveis na biblioteca de classes base (atualmente em visualização).

Você pode usar o método Array.asReadonly para retornar.

Eu acredito que a melhor prática é usar ilistau003CT> em vez de matrizes em APIs públicas por esse motivo exato. somente leitura impedirá que uma variável de membro seja definida fora do construtor, mas, como você descobriu, não impedirá que as pessoas atribuam a elementos na matriz.

Ver Matrizes consideradas um pouco prejudiciais Para maiores informações.

Editar: As matrizes não podem ser apenas leituras, mas podem ser convertidas em implementações ilists somente leitura via Array.asReadonly (), como aponta @shahkalpesh.

O .NET tende a se afastar das matrizes para todos, exceto os casos de uso mais simples e tradicionais. Para todo o resto, existem várias implementações enumeráveis/de coleção.

Quando você deseja marcar um conjunto de dados como imutável, está indo além da capacidade fornecida por uma matriz tradicional. O .NET fornece capacidade equivalente, mas não tecnicamente na forma de uma matriz. Para obter uma coleção imutável de uma matriz, use Array.AsReadOnly<T>:

var mutable = new[]
{
    'a', 'A',
    'b', 'B',
    'c', 'C',
};

var immutable = Array.AsReadOnly(mutable);

immutable será uma ReadOnlyCollection<char> instância. Como um caso de uso mais geral, você pode criar um ReadOnlyCollection<T> de qualquer genérico IList<T> implementação.

var immutable = new ReadOnlyCollection<char>(new List<char>(mutable));

Observe que deve ser uma implementação genérica; idosos simples IList Não vai funcionar, o que significa que você não pode usar esse método em uma matriz tradicional, que apenas implementa IList. Isso traz à luz a possibilidade de usar Array.AsReadOnly<T> Como um meio rápido de obter acesso a implementações genéricas que normalmente são inacessíveis por meio de uma matriz tradicional.

ReadOnlyCollection<T> dará acesso a todos os recursos que você esperaria de uma matriz imutável:

// Note that .NET favors Count over Length; all but traditional arrays use Count:
for (var i = 0; i < immutable.Count; i++)
{
    // this[] { get } is present, as ReadOnlyCollection<T> implements IList<T>:
    var element = immutable[i]; // Works

    // this[] { set } has to be present, as it is required by IList<T>, but it
    // will throw a NotSupportedException:
    immutable[i] = element; // Exception!
}

// ReadOnlyCollection<T> implements IEnumerable<T>, of course:
foreach (var character in immutable)
{
}

// LINQ works fine; idem
var lowercase =
    from c in immutable
    where c >= 'a' && c <= 'z'
    select c;

// You can always evaluate IEnumerable<T> implementations to arrays with LINQ:
var mutableCopy = immutable.ToArray();
// mutableCopy is: new[] { 'a', 'A', 'b', 'B', 'c', 'C' }
var lowercaseArray = lowercase.ToArray();
// lowercaseArray is: new[] { 'a', 'b', 'c' }

A única coisa a acrescentar é que as matrizes implicar mutabilidade. Quando você retorna uma matriz de uma função, você está sugerindo ao programador do cliente que eles podem/deve alterar as coisas.

Seguindo a resposta de Matt, IList é uma interface abstrata completa para um array, permitindo adicionar, remover, etc.Não sei por que Lippert parece sugerir isso como uma alternativa ao IEnumerable onde a imutabilidade é necessária.(Editar: porque a implementação do IList pode lançar exceções para esses métodos mutantes, se você gosta desse tipo de coisa).

Talvez outra coisa a ter em mente é que os itens da lista também podem ter um estado mutável.Se você realmente não deseja que o chamador modifique esse estado, você tem algumas opções:

Certifique-se de que os itens da lista sejam imutáveis ​​(como no seu exemplo:string é imutável).

Retorne um clone profundo de tudo; nesse caso, você poderá usar um array de qualquer maneira.

Retorne uma interface que forneça acesso somente leitura a um item:

interface IImmutable
{
    public string ValuableCustomerData { get; }
}

class Mutable, IImmutable
{
    public string ValuableCustomerData { get; set; }
}

public class Immy
{
    private List<Mutable> _mutableList = new List<Mutable>();

    public IEnumerable<IImmutable> ImmutableItems
    {
        get { return _mutableList.Cast<IMutable>(); }
    }
}

Observe que todo valor acessível a partir da interface IImmutable deve ser imutável (por exemplo,string), ou então ser uma cópia que você faz instantaneamente.

O melhor que você pode fazer é estender uma coleção existente para construir a sua. O grande problema é que ele teria que funcionar de maneira diferente de todo tipo de coleção existente, porque cada chamada teria que devolver uma nova coleção.

Você pode querer conferir minha resposta a uma pergunta semelhante para mais idéias sobre a exposição de coleções em um objeto.

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