182 votes

Pourquoi .NET String est immuable ?

Comme nous le savons tous, Chaîne de caractères est immuable. Quelles sont les raisons pour lesquelles String est immuable et l'introduction de StringBuilder comme mutable ?

237voto

Jon Hanna Points 40291
  1. Les instances de types immuables sont intrinsèquement sûres pour les threads, puisqu'aucun thread ne peut les modifier, le risque qu'un thread les modifie d'une manière qui interfère avec un autre est supprimé (la référence elle-même est une question différente).
  2. De même, le fait que l'aliasing ne puisse pas produire de changements (si x et y se réfèrent tous deux au même objet, une modification de x entraîne une modification de y) permet des optimisations considérables du compilateur.
  3. Des optimisations permettant d'économiser la mémoire sont également possibles. L'internalisation et l'atomisation sont les exemples les plus évidents, bien que nous puissions faire d'autres versions du même principe. Une fois, j'ai réalisé une économie de mémoire d'environ un demi Go en comparant des objets immuables et en remplaçant les références aux doublons afin qu'ils pointent tous vers la même instance (cela prend du temps, mais une minute de démarrage supplémentaire pour économiser une quantité massive de mémoire était un gain de performance dans le cas en question). Avec les objets mutables, cela ne peut pas être fait.
  4. Aucun effet secondaire ne peut résulter du passage d'un type immuable en tant que méthode à un paramètre, sauf s'il est out ou ref (puisque cela change la référence, pas l'objet). Un programmeur sait donc que si string x = "abc" au début d'une méthode, et que cela ne change pas dans le corps de la méthode, alors x == "abc" à la fin de la méthode.
  5. D'un point de vue conceptuel, la sémantique s'apparente davantage aux types de valeurs ; en particulier, l'égalité est basée sur l'état plutôt que sur l'identité. Cela signifie que "abc" == "ab" + "c" . Bien que cela n'exige pas l'immuabilité, le fait qu'une référence à une telle chaîne sera toujours égale à "abc" pendant toute sa durée de vie (ce qui exige l'immuabilité) rend les utilisations en tant que clés où le maintien de l'égalité avec les valeurs précédentes est vital, beaucoup plus facile à garantir la correction (les chaînes sont en effet couramment utilisées comme clés).
  6. Conceptuellement, il peut être plus logique d'être immuable. Si nous ajoutons un mois à Noël, nous n'avons pas changé Noël, nous avons produit une nouvelle date fin janvier. Il est donc logique que Christmas.AddMonths(1) produit un nouveau DateTime plutôt que d'en modifier un mutable. (Autre exemple, si je change de nom en tant qu'objet mutable, ce qui a changé c'est le nom que j'utilise, "Jon" reste immuable et les autres Jon ne seront pas affectés.
  7. La copie est rapide et simple, pour créer un clone il suffit de return this . Puisque la copie ne peut pas être modifiée de toute façon, prétendre que quelque chose est sa propre copie est sans danger.
  8. [Editer, j'avais oublié celle-ci]. L'état interne peut être partagé sans risque entre les objets. Par exemple, si vous implémentez une liste qui est soutenue par un tableau, un index de départ et un compte, alors la partie la plus coûteuse de la création d'une sous-gamme serait de copier les objets. Cependant, s'il était immuable, l'objet de sous-gamme pourrait référencer le même tableau, avec seulement l'indice de départ et le nombre devant être modifiés, avec un objet de type très un changement considérable dans le temps de construction.

Dans l'ensemble, pour les objets qui n'ont pas à subir de changement dans le cadre de leur objectif, il peut y avoir de nombreux avantages à être immuable. Le principal inconvénient réside dans le fait qu'il nécessite des constructions supplémentaires, bien que même dans ce cas, il soit souvent exagéré (rappelez-vous que vous devez faire plusieurs ajouts avant que StringBuilder devienne plus efficace que la série équivalente de concaténations, avec leur construction inhérente).

Ce serait un inconvénient si la mutabilité faisait partie de l'objectif d'un objet (qui voudrait être modélisé par un objet Employé dont le salaire ne pourrait jamais changer), bien que parfois, même dans ce cas, cela puisse être utile (dans de nombreuses applications web et autres applications sans état, le code effectuant les opérations de lecture est séparé de celui effectuant les mises à jour, et l'utilisation d'objets différents peut être naturelle - je ne rendrais pas un objet immuable pour ensuite forcer ce modèle, mais si j'avais déjà ce modèle, je pourrais rendre mes objets "lus" immuables pour le gain de performance et de garantie de correction).

La copie sur l'écriture est un terrain d'entente. Ici, la classe "réelle" contient une référence à une classe "d'état". Les classes d'état sont partagées lors des opérations de copie, mais si vous changez l'état, une nouvelle copie de la classe d'état est créée. Cette méthode est plus souvent utilisée avec le C++ qu'avec le C#, c'est pourquoi son std:string bénéficie de certains, mais pas de tous, des avantages des types immuables, tout en restant mutable.

74voto

Reed Copsey Points 315315

Rendre les chaînes de caractères immuables présente de nombreux avantages. Elle assure la sécurité automatique des threads et permet aux chaînes de se comporter comme un type intrinsèque d'une manière simple et efficace. Cela permet également de réaliser des économies supplémentaires au moment de l'exécution (par exemple en permettant un internage efficace des chaînes de caractères afin de réduire l'utilisation des ressources), et présente d'énormes avantages en matière de sécurité, puisqu'il est impossible pour un appel API tiers de modifier vos chaînes de caractères.

StringBuilder a été ajouté afin de remédier à l'inconvénient majeur des chaînes immuables : la construction de types immuables au moment de l'exécution entraîne une pression GC importante et est intrinsèquement lente. En créant une classe explicite et mutable pour gérer cela, ce problème est résolu sans ajouter de complications inutiles à la classe string.

21voto

Carlos Muñoz Points 6208

Les chaînes de caractères ne sont pas vraiment immuables. Elles sont simplement publiquement immuables. Cela signifie que vous ne pouvez pas les modifier à partir de leur interface publique. Mais à l'intérieur, elles sont en fait mutables.

Si vous ne me croyez pas, regardez la définition de String.Concat en utilisant réflecteur . Les dernières lignes sont...

int length = str0.Length;
string dest = FastAllocateString(length + str1.Length);
FillStringChecked(dest, 0, str0);
FillStringChecked(dest, length, str1);
return dest;

Comme vous pouvez le constater, le FastAllocateString renvoie une chaîne vide mais allouée, puis elle est modifiée par FillStringChecked

En fait, le FastAllocateString est une méthode externe et la méthode FillStringChecked n'est pas sûr, il utilise donc des pointeurs pour copier les octets.

Il existe peut-être de meilleurs exemples, mais c'est celui que j'ai trouvé jusqu'à présent.

14voto

kolosy Points 1265

La gestion des chaînes est un processus coûteux. Le fait de garder les chaînes immuables permet de réutiliser les chaînes répétées, plutôt que de les recréer.

13voto

NebuSoft Points 2408

Pourquoi les types de chaînes sont immuables en C#

String est un type de référence, il n'est donc jamais copié, mais transmis par référence. Comparez cela à l'objet C++ std::string (qui n'est pas immuable), qui est est transmis par valeur. Cela signifie que si vous voulez utiliser une chaîne comme clé dans une Hashtable, tout va bien en C++, car le langage C++ copiera la chaîne pour stocker la dans la table de hachage (en fait std::hash_map, mais tout de même) pour une comparaison ultérieure. Ainsi, même si vous modifiez plus tard modifiez l'instance de std::string, vous n'avez rien à craindre. Mais en .Net, lorsque vous utilisez une chaîne dans une table de hachage, celle-ci stocke une référence à cette instance. Maintenant supposez pour un moment que les chaînes ne sont pas immuables, et voyez ce qui ce qui se passe : 1. Quelqu'un insère une valeur x avec la clé "hello" dans une Hashtable. 2. La table de hachage calcule la valeur de hachage de la chaîne et place une référence à la chaîne et à la valeur de hachage. référence à la chaîne et à la valeur x dans le godet approprié. 3. L'utilisateur modifie l'instance de String pour qu'elle soit "bye". 4. Maintenant quelqu'un veut la valeur dans la table de hachage associée à "hello". Il finit par chercher dans le bon seau, mais en comparant les chaînes de caractères, il dit "bye"!="hello", et donc aucune valeur n'est retournée. 5. Peut-être que quelqu'un veut la valeur "bye" ? "bye" a probablement un différent, donc la table de hachage cherchera dans un dans un seau différent. Aucune clé "bye" dans ce seau, donc notre entrée n'est toujours pas trouvée.

Rendre les chaînes immuables signifie que l'étape 3 est impossible. Si quelqu'un modifie la chaîne, il crée une nouvel objet chaîne, laissant l'ancien seul. Ce qui signifie que la clé dans la hashtable est toujours "hello", et donc toujours correcte.

Donc, probablement parmi d'autres choses, les chaînes immuables sont un moyen de permettre aux chaînes passées par référence d'être utilisées comme clés dans une table de hachage ou objet dictionnaire similaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X