Domanda

È possibile in qualche modo contrassegnare un System.Array come immutabile. Se messi dietro un set pubblico / privato a cui non possono essere aggiunti, dal momento che richiede una nuova allocazione e una nuova assegnazione, ma un consumatore può comunque impostare qualsiasi pedice che desidera:

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

Pensavo che la parola chiave readonly potesse fare il trucco, ma senza fortuna.

È stato utile?

Soluzione

ReadOnlyCollection<T> è probabilmente quello che stai cercando. Non ha un metodo Add().

Altri suggerimenti

Le Linee guida per la progettazione del framework suggeriscono di restituire una copia dell'array. In questo modo, i consumatori non possono cambiare gli articoli dall'array.

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

questi sono migliori:

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

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

Gli esempi sono direttamente dal libro.

Vedi Collezioni immutabili ora disponibili nella libreria della classe base (attualmente in anteprima).

È possibile utilizzare il metodo Array.AsReadOnly per restituire.

Credo che la migliore pratica sia usare IList < T > piuttosto che array nelle API pubbliche per questo motivo esatto. di sola lettura impedirà l'impostazione di una variabile membro all'esterno del costruttore, ma come hai scoperto, non impedirà alle persone di assegnare elementi nell'array.

Vedi Array considerati in qualche modo dannosi per ulteriori informazioni.

Modifica: le matrici non possono essere di sola lettura, ma possono essere convertite in implementazioni IList di sola lettura tramite Array.AsReadOnly () come sottolinea @shahkalpesh.

.NET tende ad allontanarsi dagli array per tutti tranne i casi d'uso più semplici e tradizionali. Per tutto il resto, ci sono varie implementazioni enumerabili / di raccolta.

Quando si desidera contrassegnare un set di dati come immutabili, si va oltre le funzionalità fornite da un array tradizionale. .NET offre funzionalità equivalenti, ma non tecnicamente sotto forma di un array. Per ottenere una raccolta immutabile da un array, utilizzare Array.AsReadOnly<T> :

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

var immutable = Array.AsReadOnly(mutable);

immutable sarà un ReadOnlyCollection<char> . Come caso d'uso più generale, è possibile creare un ReadOnlyCollection<T> da qualsiasi generico IList<T> .

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

Nota che deve essere un'implementazione generica; vecchio <= > non funzionerà, il che significa che non è possibile utilizzare questo metodo su un array tradizionale, che implementa solo IList . Ciò mette in luce la possibilità di utilizzare <=> come mezzo rapido per ottenere l'accesso a implementazioni generiche normalmente inaccessibili tramite un array tradizionale.

<=> ti darà accesso a tutte le funzionalità che ti aspetteresti da un array immutabile:

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

L'unica cosa da aggiungere è che la mutabilità implica di array. Quando restituisci un array da una funzione, stai suggerendo al programmatore client che possono / dovrebbero cambiare le cose.

Oltre alla risposta di Matt, IList è un'interfaccia astratta completa per un array, quindi consente di aggiungere, rimuovere, ecc. Non sono sicuro del motivo per cui Lippert sembra suggerirlo come un'alternativa a IEnumerable dove è necessaria l'immutabilità. ( Modifica: perché l'implementazione di IList può generare eccezioni per quei metodi mutanti, se ti piace quel tipo di cose).

Forse un'altra cosa da tenere presente che gli elementi nell'elenco potrebbero anche avere uno stato mutabile. Se davvero non vuoi che il chiamante modifichi tale stato, hai alcune opzioni:

Assicurati che gli elementi dell'elenco siano immutabili (come nel tuo esempio: string è immutabile).

Restituisce un clone profondo di tutto, quindi in tal caso è possibile utilizzare comunque un array.

Restituisce un'interfaccia che dà accesso in sola lettura 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>(); }
    }
}

Nota che ogni valore accessibile dall'interfaccia IImmutable deve essere di per sé immutabile (ad es. stringa), oppure essere una copia che fai al volo.

Il meglio che puoi sperare di fare è estendere una collezione esistente per crearne una tua. Il grosso problema è che dovrebbe funzionare in modo diverso rispetto a qualsiasi tipo di raccolta esistente perché ogni chiamata dovrebbe restituire una nuova raccolta.

Potresti voler dare un'occhiata a la mia risposta a una simile domanda per ulteriori idee sull'esposizione di raccolte su un oggetto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top