web-dev-qa-db-fra.com

Comment gérer les setters sur des champs immuables?

J'ai une classe avec deux readonly int des champs. Ils sont exposés en tant que propriétés:

public class Thing
{
    private readonly int _foo, _bar;

    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public int Foo { get { return _foo; } set { } }

    public int Bar { get { return _bar; } set { } }
}

Cependant, cela signifie que le code suivant est parfaitement légal:

Thing iThoughtThisWasMutable = new Thing(1, 42);

iThoughtThisWasMutable.Foo = 99;  // <-- Poor, mistaken developer.
                                  //     He surely has bugs now. :-(

Les bogues qui proviennent de en supposant que cela fonctionnerait seront certainement insidieux. Bien sûr, le développeur erroné aurait dû lire les documents. Mais cela ne change pas le fait qu'aucune erreur de compilation ou d'exécution ne l'a averti du problème.

Comment la classe Thing doit-elle être modifiée pour que les développeurs soient moins susceptibles de faire l'erreur ci-dessus?

Jetez une exception? Utilisez une méthode getter au lieu d'une propriété?

25
kdbanman

Pourquoi rendre ce code légal?
Sortez le set { } s'il ne fait rien.
Voici comment définir une propriété publique en lecture seule:

public int Foo { get { return _foo; } }
106
paparazzo

Avec C # 5 et avant, nous étions confrontés à deux options pour les champs immuables exposés via un getter:

  1. Créez une variable de support en lecture seule et renvoyez-la via un getter manuel. Cette option est sécurisée (il faut supprimer explicitement le readonly pour détruire l'immuabilité. Cependant, elle a créé beaucoup de code de plaque de chaudière.
  2. Utilisez une propriété automatique, avec un setter privé. Cela crée du code plus simple, mais il est plus facile de casser accidentellement l'immuabilité.

Cependant, avec C # 6 (disponible dans VS2015, qui a été publié hier), nous obtenons maintenant le meilleur des deux mondes: les propriétés automatiques en lecture seule. Cela nous permet de simplifier la classe OP pour:

public class Thing
{
    /// <summary> I AM IMMUTABLE. </summary>
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; }
    public int Bar { get; }
}

Le Foo = foo et Bar = bar les lignes ne sont valides que dans le constructeur (qui satisfait à l'exigence robuste en lecture seule) et le champ de support est implicite, plutôt que d'avoir à être explicitement défini (ce qui permet d'obtenir le code le plus simple).

44
David Arno

Vous pourriez simplement vous débarrasser des colons. Ils ne font rien, ils dérouteront les utilisateurs et entraîneront des bugs. Cependant, vous pouvez plutôt les rendre privés et ainsi vous débarrasser des variables de support, simplifiant votre code:

public class Thing
{
    public Thing(int foo, int bar)
    {
        Foo = foo;
        Bar = bar;
    }

    public int Foo { get; private set; }

    public int Bar { get; private set; }
}
12
David Arno

Deux solutions.

Simple:

N'incluez pas les setters comme l'a noté David pour les objets immuables en lecture seule.

Alternativement:

Permet aux setters de renvoyer un nouvel objet immuable, verbeux par rapport à l'ancien mais fournit un état dans le temps pour chaque objet initialisé. Cette conception est un outil très utile pour la sécurité et l'immuabilité des threads qui s'étend sur toutes les langues impératives OOP.

Pseudocode

public class Thing
{

{readonly vars}

    public Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}

sine statique publique

public class Thing
{

{readonly vars}

    private Thing(int foo, int bar)
    {
        _foo = foo;
        _bar = bar;
    }

    public static with(int foo, int bar) {
        return new Thing(foo, bar)
    }

    public Thing withFoo(int foo) {
        return new Thing(foo, getBar());
    }

    public Thing withBar(int bar) {
        return new Thing(getFoo(), bar)
    }

    etc...
}
6
Matt Smithies