web-dev-qa-db-fra.com

Comment créer une classe immuable?

Je travaille sur la création d'une classe immuable.
J'ai marqué toutes les propriétés en lecture seule.

J'ai une liste d'articles dans la classe.
Bien que si la propriété est en lecture seule, la liste peut être modifiée.

L'exposition de l'IEnumerable de la liste la rend immuable.
Je voulais savoir quelles sont les règles de base que l'on doit suivre pour rendre une classe immuable?

106
Biswanath

Je pense que vous êtes sur la bonne voie -

  • toutes les informations injectées dans la classe doivent être fournies dans le constructeur
  • toutes les propriétés doivent être des getters uniquement
  • si une collection (ou un tableau) est passée dans le constructeur, elle doit être copiée pour empêcher l'appelant de la modifier plus tard
  • si vous souhaitez renvoyer votre collection, renvoyez une copie ou une version en lecture seule (par exemple, en utilisant ArrayList.ReadOnly ou similaire - vous pouvez combiner cela avec le point précédent et stocker une copie en lecture seule à retourner lorsque les appelants y accèdent), renvoyer un énumérateur ou utiliser une autre méthode/propriété qui autorise l'accès en lecture seule à la collection
  • gardez à l'esprit que vous pouvez toujours avoir l'apparence d'une classe mutable si l'un de vos membres est mutable - si tel est le cas, vous devez copier tout état que vous souhaitez conserver et éviter de renvoyer des objets mutables entiers, sauf si vous les copiez avant de les rendre à l'appelant - une autre option est de ne renvoyer que des "sections" immuables de l'objet mutable - merci à @Brian Rasmussen de m'avoir encouragé à développer ce point
116
Blair Conrad

Pour être immuable, toutes vos propriétés et champs doivent être en lecture seule. Et les éléments de toute liste doivent eux-mêmes être immuables.

Vous pouvez créer une propriété de liste en lecture seule comme suit:

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}
17
Joe

N'oubliez pas non plus que:

public readonly object[] MyObjects;

n'est pas immuable même s'il est marqué avec un mot clé en lecture seule. Vous pouvez toujours modifier les références/valeurs de tableau individuelles par l'accesseur d'index.

11
lubos hasko

Utilisez la classe ReadOnlyCollection. Il est situé dans le System.Collections.ObjectModel espace de noms.

Sur tout ce qui renvoie votre liste (ou dans le constructeur), définissez la liste comme une collection en lecture seule.

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}
4
mbillard

Une autre option serait d'utiliser un modèle de visiteur au lieu d'exposer des collections internes.

1
Andrew