Pregunta

¿Es posible marcar de alguna manera un System.Array como inmutable? Cuando se colocan detrás de un conjunto público-privado / privado, no se pueden agregar, ya que requiere reasignación y reasignación, pero un consumidor aún puede establecer cualquier subíndice que desee:

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

Pensé que la palabra clave readonly podría ser la solución, pero no tuve tanta suerte.

¿Fue útil?

Solución

ReadOnlyCollection<T> es probablemente lo que está buscando. No tiene un método Add().

Otros consejos

Las Directrices de diseño del marco sugieren devolver una copia de la matriz. De esa manera, los consumidores no pueden cambiar los elementos de la matriz.

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

estos son mejores:

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

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

Los ejemplos son directamente del libro.

Consulte Colecciones inmutables ahora disponibles en la biblioteca de clase base (actualmente en vista previa).

Puede usar el método Array.AsReadOnly para regresar.

Creo que la mejor práctica es usar IList < T > en lugar de matrices en API públicas por esta razón exacta. readonly evitará que una variable miembro se establezca fuera del constructor, pero como descubrió, no evitará que las personas asignen elementos en la matriz.

Ver Matrices consideradas algo dañinas para más información.

Editar: Las matrices no se pueden leer solo, pero se pueden convertir en implementaciones de IList de solo lectura a través de Array.AsReadOnly () como señala @shahkalpesh.

.NET tiende a alejarse de las matrices para todos, excepto los casos de uso más simples y tradicionales. Para todo lo demás, hay varias implementaciones enumerables / de colección.

Cuando desea marcar un conjunto de datos como inmutable, va más allá de la capacidad que brinda una matriz tradicional. .NET proporciona una capacidad equivalente, pero no técnicamente en forma de matriz. Para obtener una colección inmutable de una matriz, use Array.AsReadOnly<T> :

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

var immutable = Array.AsReadOnly(mutable);

immutable será un ReadOnlyCollection<char> instancia. Como un caso de uso más general, puede crear un ReadOnlyCollection<T> de cualquier genérico IList<T> implementación.

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

Tenga en cuenta que tiene que ser una implementación genérica; <= > no funcionará, lo que significa que no puede usar este método en una matriz tradicional, que solo implementa IList . Esto pone de manifiesto la posibilidad de utilizar <=> como un medio rápido para obtener acceso a implementaciones genéricas que normalmente son inaccesibles a través de una matriz tradicional.

<=> le dará acceso a todas las funciones que esperaría de una matriz inmutable:

// 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' }

Lo único que se puede agregar es que las matrices implican mutabilidad. Cuando devuelve una matriz de una función, está sugiriendo al programador del cliente que pueden / deberían cambiar las cosas.

Además de la respuesta de Matt, IList es una interfaz abstracta completa para una matriz, por lo que permite agregar, eliminar, etc. No estoy seguro de por qué Lippert parece sugerirlo como una alternativa a IEnumerable donde se necesita la inmutabilidad. ( Editar: porque la implementación de IList puede generar excepciones para esos métodos de mutación, si te gusta ese tipo de cosas).

Quizás otra cosa a tener en cuenta es que los elementos de la lista también pueden tener un estado mutable. Si realmente no desea que la persona que llama modifique dicho estado, tiene algunas opciones:

Asegúrese de que los elementos de la lista sean inmutables (como en su ejemplo: la cadena es inmutable).

Devuelve un clon profundo de todo, por lo que en ese caso podrías usar una matriz de todos modos.

Devuelve una interfaz que da acceso de solo lectura a un elemento:

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>(); }
    }
}

Tenga en cuenta que cada valor accesible desde la interfaz IImmutable debe ser inmutable (p. ej., cadena) o, de lo contrario, debe ser una copia que realice sobre la marcha.

Lo mejor que puede hacer es extender una colección existente para construir la suya. El gran problema es que tendría que funcionar de manera diferente a cada tipo de colección existente porque cada llamada tendría que devolver una nueva colección.

Es posible que desee consultar mi respuesta a una similar pregunta para obtener más ideas sobre cómo exponer colecciones en un objeto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top