J'étais juste curieux de savoir pourquoi les structures, les chaînes, etc. sont immuables? Quelle est la raison de les rendre immuables et le reste des objets comme mutable. Quelles sont les choses qui sont considérées comme rendant un objet immuable?
Y a-t-il une différence dans la façon dont la mémoire est allouée et désallouée pour les objets mutables et immuables?
Si ce sujet vous intéresse, j'ai un certain nombre d'articles sur la programmation immuable à http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/
J'étais juste curieux de savoir pourquoi les structures, les chaînes, etc. sont immuables?
Les structures et les classes ne sont pas immuables par défaut, bien qu'il soit recommandé de rendre les structures immuables. J'aime aussi les cours immuables.
Les cordes sont immuables.
Quelle est la raison de les rendre immuables et le reste des objets comme mutable.
Il est plus facile de raisonner sur des objets qui ne changent pas. Si j'ai une file d'attente avec trois éléments, je sais qu'elle n'est pas vide maintenant, elle n'était pas vide il y a cinq minutes, elle ne sera pas vide à l'avenir. C'est immuable! Une fois que je connais un fait à ce sujet, je peux l'utiliser pour toujours. Les faits concernant les objets immuables ne deviennent pas périmés.
Un cas particulier du premier point: les objets immuables sont beaucoup plus faciles à rendre threadsafe. La plupart des problèmes de sécurité des threads sont dus aux écritures sur un thread et aux lectures sur un autre; les objets immuables n'ont pas d'écritures.
Les objets immuables peuvent être démontés et réutilisés. Par exemple, si vous avez un arbre binaire immuable, vous pouvez utiliser ses sous-arbres gauche et droit comme sous-arbres d'un arbre différent sans vous en soucier. Dans une structure modifiable, vous finissez généralement par faire des copies de données pour les réutiliser, car vous ne voulez pas que des modifications apportées à un objet logique en affectent un autre. Cela peut économiser beaucoup de temps et de mémoire.
Il y a beaucoup de raisons de rendre les structures immuables. En voici un.
Les structures sont copiées par valeur et non par référence. Il est facile de traiter accidentellement une structure comme étant copiée par référence. Par exemple:
void M()
{
S s = whatever;
... lots of code ...
s.Mutate();
... lots more code ...
Console.WriteLine(s.Foo);
...
}
Maintenant, vous voulez refactoriser une partie de ce code dans une méthode d'assistance:
void Helper(S s)
{
... lots of code ...
s.Mutate();
... lots more code ...
}
FAUX! Cela devrait être (ref S s) - si vous ne le faites pas, la mutation se produira sur une copie de s. Si vous n'autorisez pas les mutations en premier lieu, toutes ces sortes de problèmes disparaissent.
Rappelez-vous mon premier point sur les faits concernant les structures immuables qui restent des faits?
Supposons que la chaîne soit modifiable:
public static File OpenFile(string filename)
{
if (!HasPermission(filename)) throw new SecurityException();
return InternalOpenFile(filename);
}
Que faire si l'appelant hostile mute le nom de fichier après le contrôle de sécurité et avant le le fichier est ouvert? Le code vient d'ouvrir un fichier auquel ils ne sont peut-être pas autorisés!
Encore une fois, il est difficile de raisonner sur les données mutables. Vous voulez que le fait "cet appelant soit autorisé à voir le fichier décrit par cette chaîne" soit vrai pour toujours , pas jusqu'à ce qu'une mutation se produise . Avec des chaînes mutables, pour écrire du code sécurisé, nous devions constamment faire des copies de données que nous savons ne pas changer.
Quelles sont les choses qui sont considérées comme rendant un objet immuable?
Le type représente-t-il logiquement quelque chose qui est une valeur "éternelle"? Le nombre 12 est le nombre 12; ça ne change pas. Les entiers doivent être immuables. Le point (10, 30) est le point (10, 30); ça ne change pas. Les points doivent être immuables. La chaîne "abc" est la chaîne "abc"; ça ne change pas. Les chaînes doivent être immuables. La liste (10, 20, 30) ne change pas. Etc.
Parfois, le type représente des choses qui changent. Le nom de famille de Mary Smith est Smith, mais demain elle pourrait être Mary Jones. Ou Miss Smith aujourd'hui pourrait être le docteur Smith demain. L'étranger a maintenant cinquante points de vie mais en a dix après avoir été touché par le faisceau laser. Certaines choses sont mieux représentées comme des mutations.
Y a-t-il une différence dans la façon dont la mémoire est allouée et désallouée pour les objets mutables et immuables?
Pas comme tel. Comme je l'ai mentionné auparavant, l'une des bonnes choses à propos des valeurs immuables est que vous pouvez en réutiliser certaines parties sans faire de copies. Donc, dans ce sens, l'allocation de mémoire peut être très différente.
Les structures non ... c'est pourquoi les structures mutables sont mauvaises.
La création de structures mutables peut conduire à toutes sortes de comportements étranges dans votre application et, par conséquent, elles sont considérées comme une très mauvaise idée (car elles ressemblent à un type de référence mais sont en fait un type de valeur et seront copiées chaque fois que vous passerez les autour).
Les cordes, en revanche, le sont. Cela les rend intrinsèquement thread-safe et permet des optimisations via l'internement de chaînes. Si vous devez construire une chaîne compliquée à la volée, vous pouvez utiliser StringBuilder
.
Les concepts de mutabilité et d'immutabilité ont des significations différentes lorsqu'ils sont appliqués aux structures et aux classes. Un aspect clé (souvent, la principale faiblesse) des classes mutables est si Foo
a un champ Bar
de type List<Integer>
, qui contient une référence à une liste contenant (1,2,3), un autre code qui a une référence à cette même liste pourrait la modifier, de telle sorte que Bar
contient une référence à une liste contenant (4 , 5,6), même si cet autre code n'a aucun accès à Bar
. En revanche, si Foo
avait un champ Biz
de type System.Drawing.Point
, la seule façon dont quelque chose pourrait modifier n'importe quel aspect de Biz
serait d'avoir un accès en écriture à ce champ .
Les champs (publics et privés) d'une structure peuvent être mutés par n'importe quel code qui peut muter l'emplacement de stockage dans lequel la structure est stockée, et ne peuvent pas être mutés par un code qui ne peut pas muter l'emplacement de stockage où elle est stockée. Si toutes les informations encapsulées dans une structure sont conservées dans ses champs, une telle structure peut effectivement combiner le contrôle d'un type immuable avec la commodité d'un type mutable, sauf si la structure est codée de manière à supprimer cette commodité ( une habitude que, malheureusement, certains programmeurs Microsoft recommandent).
Le "problème" avec les structures est que lorsqu'une méthode (y compris une implémentation de propriété) est invoquée sur une structure dans un contexte en lecture seule (ou un emplacement immuable), le système copie la structure, exécute la méthode sur la copie temporaire et en silence jette le résultat. Ce comportement a conduit les programmeurs à émettre l'idée malheureuse que la façon d'éviter les problèmes avec les méthodes de mutation est d'avoir de nombreuses structures interdisant les mises à jour par morceaux, alors que les problèmes auraient pu être mieux évités en en remplaçant simplement les propriétés avec des champs exposés .
Soit dit en passant, certaines personnes se plaignent que lorsqu'une propriété de classe renvoie une structure commodément mutable, les modifications apportées à la structure n'affectent pas la classe dont elle provient. Je suppose que c'est une bonne chose - le fait que l'élément retourné est une structure rend le comportement clair (surtout s'il s'agit d'une structure à champ exposé). Comparez un extrait à l'aide d'une structure et d'une propriété hypothétiques sur Drawing.Matrix
avec une utilisant une propriété réelle sur cette classe telle qu'implémentée par Microsoft:
// Structure hypothétique Structure publique { Public float xx, xy, yx, yy, dx, dy; } Transform2d; // Propriété hypothétique de "System.Drawing.Drawing2d.Matrix" Public Transform2d Transform {get;} // Propriété réelle de "System.Drawing. Drawing2d.Matrix " Public float [] Elements {get; } // Code utilisant une structure hypothétique Transform2d myTransform = myMatrix.Transform; MyTransform.dx + = 20; ... autre code utilisant myTransform // Code utilisant la propriété Microsoft réelle float [] myArray = myMatrix.Elements; myArray [4] + = 20; ... autre code utilisant myArray
En regardant la propriété Microsoft réelle, existe-t-il un moyen de savoir si l'écriture dans myArray[4]
affectera myMatrix
? Même en regardant la page http://msdn.Microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx est-il possible de le savoir? Si la propriété avait été écrite en utilisant l'équivalent basé sur une structure, il n'y aurait pas de confusion; la propriété qui retourne la structure ne retournerait ni plus ni moins que la valeur actuelle de six nombres. En changeant myTransform.dx
ne serait ni plus ni moins qu'une écriture dans une variable à virgule flottante qui n'était attachée à rien d'autre. Quiconque n'aime pas le fait que changer myTransform.dx
n'affecte pas myMatrix
devrait être aussi ennuyé que l'écriture myArray[4]
n'affecte pas non plus myMatrix
, sauf que l'indépendance de myMatrix
et myTransform
est apparente, tandis que l'indépendance de myMatrix
et myArray
n'est pas.
Un type struct n'est pas immuable. Oui, les cordes le sont. Rendre votre propre type immuable est facile, ne fournissez simplement pas de constructeur par défaut, rendez tous les champs privés et ne définissez aucune méthode ou propriété qui modifie une valeur de champ. Avoir une méthode qui devrait muter l'objet renvoyer un nouvel objet à la place. Il y a un angle de gestion de la mémoire, vous avez tendance à créer beaucoup de copies et de déchets.
Les structures peuvent être mutables, mais c'est une mauvaise idée car elles ont une sémantique de copie. Si vous apportez une modification à une structure, vous pouvez en fait modifier une copie. Il est très difficile de suivre exactement ce qui a été changé.
Les structures mutables engendrent des erreurs.