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é?
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; } }
Avec C # 5 et avant, nous étions confrontés à deux options pour les champs immuables exposés via un getter:
readonly
pour détruire l'immuabilité. Cependant, elle a créé beaucoup de code de plaque de chaudière.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).
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; }
}
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...
}