Question

Est-il possible en quelque sorte de marquer un System.Array comme immuable. Lorsqu'elles sont placées derrière un ensemble public-get / privé, elles ne peuvent pas être ajoutées, car elles nécessitent une réattribution et une réaffectation, mais un consommateur peut toujours définir le sous-indice de son choix:

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

Je pensais que le readonly mot-clé pourrait faire l'affaire, mais pas de chance.

Était-ce utile?

La solution

ReadOnlyCollection<T> est probablement ce que vous recherchez. Il n'a pas de Add() méthode.

Autres conseils

Les consignes de conception de structure suggèrent de renvoyer une copie du tableau. Ainsi, les consommateurs ne peuvent pas modifier les éléments du tableau.

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

ce sont mieux:

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

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

Les exemples sont tirés directement du livre.

Veuillez consulter Les collections immuables maintenant disponibles dans la bibliothèque de classes de base (actuellement en aperçu).

Vous pouvez utiliser la méthode Array.AsReadOnly pour renvoyer.

Je pense que la meilleure pratique consiste à utiliser IList < T > plutôt que des tableaux dans les API publiques pour cette raison exacte. readonly empêchera une variable membre d'être définie en dehors du constructeur, mais comme vous l'avez découvert, cela n'empêchera pas les gens d'assigner des éléments au tableau.

Voir Tableaux considérés comme assez dangereux pour plus d'informations.

Modifier: Les tableaux ne peuvent pas être lus uniquement, mais ils peuvent être convertis en implémentations IList en lecture seule via Array.AsReadOnly (), comme le souligne @shahkalpesh.

.NET a tendance à s'éloigner des tableaux pour tous les cas d'utilisation, sauf les plus simples et les plus traditionnels. Pour tout le reste, il existe différentes implémentations de énumération / collection.

Lorsque vous souhaitez marquer un ensemble de données comme immuable, vous allez au-delà des capacités fournies par un tableau traditionnel. .NET fournit une capacité équivalente, mais pas techniquement sous la forme d'un tableau. Pour obtenir une collection immuable d'un tableau, utilisez Array.AsReadOnly<T> :

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

var immutable = Array.AsReadOnly(mutable);

immutable sera un ReadOnlyCollection<char> . En règle générale, vous pouvez créer un ReadOnlyCollection<T> à partir d'un générique IList<T> .

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

Notez qu'il doit s'agir d'une implémentation générique; vieux vieux <= > ne fonctionnera pas, ce qui signifie que vous ne pouvez pas utiliser cette méthode sur un tableau traditionnel, qui implémente uniquement IList . Cela met en lumière la possibilité d'utiliser <=> pour obtenir un accès rapide aux implémentations génériques normalement inaccessibles via un tableau traditionnel.

<=> vous donnera accès à toutes les fonctionnalités attendues d'un tableau immuable:

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

La seule chose à ajouter est que les tableaux impliquent une mutabilité. Lorsque vous renvoyez un tableau à partir d’une fonction, vous suggérez au programmeur client qu’il peut / devrait changer les choses.

Suite à la réponse de Matt, IList est une interface abstraite complète pour un tableau. Il permet donc d’ajouter, de supprimer, etc. Je ne sais pas pourquoi Lippert semble le suggérer comme une alternative à IEnumerable lorsque l’immuabilité est nécessaire. ( Modifier , car l'implémentation IList peut générer des exceptions pour ces méthodes de mutation, si vous aimez ce genre de chose).

Peut-être une autre chose à garder à l'esprit que les éléments de la liste peuvent aussi avoir l'état mutable. Si vous ne voulez vraiment pas que l'appelant modifie un tel état, vous avez quelques options:

Assurez-vous que les éléments de la liste sont immuables (comme dans votre exemple: la chaîne est immuable).

Renvoyez un clone complet de tout, vous pouvez donc utiliser un tableau quand même.

Renvoie une interface donnant accès à un élément en lecture seule:

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

Notez que chaque valeur accessible à partir de l'interface IImmutable doit elle-même être immuable (par exemple, une chaîne de caractères), ou être une copie réalisée à la volée.

Le mieux que vous puissiez espérer est d’étendre une collection existante pour créer la vôtre. Le gros problème est que cela devrait fonctionner différemment de chaque type de collection existant, car chaque appel devrait renvoyer une nouvelle collection.

Vous pouvez consulter ma réponse à une réponse similaire. question pour plus d'idées sur l'exposition de collections sur un objet.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top