Quelle est la différence entre une chaîne mutable et immuable en C #?

Réponses:

315

Mutable et immuable sont des mots anglais signifiant respectivement «peut changer» et «ne peut pas changer». Le sens des mots est le même dans le contexte informatique; c'est à dire

  • une chaîne mutable peut être modifiée, et
  • une chaîne immuable ne peut pas être modifiée.

La signification de ces mots est la même en C # / .NET que dans d'autres langages / environnements de programmation, bien que (évidemment) les noms des types puissent différer, tout comme d'autres détails.


Pour mémoire:

  • String est le type de chaîne immuable standard C # / .Net
  • StringBuilder est le type de chaîne mutable standard C # / .Net

Pour "effectuer une modification" sur une chaîne représentée sous forme de C # String, vous créez en fait un nouvel Stringobjet. L'original Stringn'est pas modifié ... car il est immuable.

Dans la plupart des cas, il est préférable d'utiliser Stringcar c'est une raison plus facile à leur sujet; par exemple, vous n'avez pas besoin de considérer la possibilité qu'un autre thread puisse "changer ma chaîne". Cependant, lorsque vous devez construire ou modifier une chaîne à l'aide d'une séquence d'opérations, il peut être plus efficace d'utiliser un fichier StringBuilder.


Et enfin, pour ceux qui affirment que a StringBuildern'est pas une chaîne car elle n'est pas immuable, la documentation Microsoft décrit StringBuilderainsi:

"Représente une chaîne de caractères mutable . Cette classe ne peut pas être héritée."

Stephen C
la source
182

String est immuable

c'est-à-dire que les chaînes ne peuvent pas être modifiées. Lorsque vous modifiez une chaîne (en y ajoutant par exemple), vous créez en fait une nouvelle chaîne.

Mais StringBuildern'est pas immuable (plutôt, il est mutable)

donc si vous devez modifier une chaîne plusieurs fois, par exemple plusieurs concaténations, utilisez StringBuilder.

Pankaj Agarwal
la source
4
Que se passe-t-il si vous concattez des chaînes immuables plusieurs fois? Des charges d'utilisation de la mémoire pour les chaînes sans références? Ou simplement lent?
Rudie
13
@Rudie - les deux. Chaque concaténation crée un nouvel objet String et copie tous les caractères des deux chaînes d'entrée. Leads to O(N^2)behavior ...
Stephen C
@StephenC expliqué avec les mots-clés.
Kent Liau
6
si vous devez modifier une chaîne plusieurs fois, comme plusieurs concaténations, utilisez StringBuilder ... Cela m'a totalement éclairci l'esprit ...
katmanco
45

Un objet est mutable si, une fois créé, son état peut être changé en appelant diverses opérations sur lui, sinon il est immuable.

Chaîne immuable

En C # (et .NET), une chaîne est représentée par la classe System.String. Le stringmot-clé est un alias pour cette classe.

La classe System.String est immuable, c'est-à-dire qu'une fois créée, son état ne peut pas être modifié .

Donc , toutes les opérations que vous effectuez sur une chaîne comme Substring, Remove, Replace, concaténation en utilisant « + » opérateur etc créera une nouvelle chaîne et le retourner.

Voir le programme suivant pour une démonstration -

string str = "mystring";
string newString = str.Substring(2);
Console.WriteLine(newString);
Console.WriteLine(str);

Cela affichera respectivement «string» et «mystring».

Pour les avantages de l'immuabilité et pourquoi les chaînes sont immuables, vérifiez pourquoi .NET String est immuable? .

Chaîne mutable

Si vous souhaitez avoir une chaîne que vous souhaitez modifier souvent, vous pouvez utiliser la StringBuilderclasse. Les opérations sur une StringBuilder instance modifieront le même objet .

Pour plus de conseils sur l' utilisation, StringBuilder reportez-vous à Quand utiliser StringBuilder? .

Unmesh Kondolikar
la source
Belle explication!
Hari
36

Tous les stringobjets sont immuables en C #. Les objets de la classe string, une fois créés, ne peuvent jamais représenter une valeur autre que celle avec laquelle ils ont été construits. Toutes les opérations qui semblent "changer" une chaîne en produisent une nouvelle. Ceci est inefficace avec la mémoire, mais extrêmement utile pour pouvoir être sûr qu'une chaîne ne changera pas de forme sous vous - car tant que vous ne changez pas votre référence, la chaîne à laquelle vous faites référence ne changera jamais.

Un objet mutable, en revanche, a des champs de données qui peuvent être modifiés. Une ou plusieurs de ses méthodes changeront le contenu de l'objet, ou il a une propriété qui, une fois écrite dans, changera la valeur de l'objet.

Si vous avez un objet mutable - le plus similaire à String est StringBuffer- vous devez en faire une copie si vous voulez être absolument sûr qu'il ne changera pas sous vous. C'est pourquoi les objets mutables sont dangereux à utiliser comme clés dans n'importe quelle forme Dictionaryou ensemble - les objets eux-mêmes pourraient changer et la structure de données n'aurait aucun moyen de le savoir, conduisant à des données corrompues qui finiraient par planter votre programme.

Cependant, vous pouvez changer son contenu, donc c'est beaucoup plus efficace en mémoire que de faire une copie complète parce que vous vouliez changer un seul caractère, ou quelque chose de similaire.

En règle générale, la bonne chose à faire est d'utiliser des objets mutables pendant que vous créez quelque chose, et des objets immuables une fois que vous avez terminé. Cela s'applique aux objets qui ont des formes immuables, bien sûr; la plupart des collections ne le font pas. Cependant, il est souvent utile de fournir des formes de collections en lecture seule, ce qui équivaut à immuable, lors de l'envoi de l'état interne de votre collection à d'autres contextes, sinon quelque chose pourrait prendre cette valeur de retour, y faire quelque chose et corrompre votre Les données.

Adam Norberg
la source
12

Immuable:

Lorsque vous effectuez une opération sur un objet, cela crée un nouvel objet, par conséquent, l'état n'est pas modifiable comme dans le cas d'une chaîne.

Mutable

Lorsque vous effectuez une opération sur un objet, l'objet lui-même ne modifie aucun nouvel objet créé comme dans le cas de StringBuilder

TalentTuner
la source
8

Dans .NET System.String (aka string) est un objet immuable. Cela signifie que lorsque vous créez un objet, vous ne pouvez pas modifier sa valeur par la suite. Vous ne pouvez recréer qu'un objet immuable.

System.Text.StringBuilder est l'équivalent mutable de System.String et vous pouvez modifier sa valeur

Par exemple:

class Program
{
    static void Main(string[] args)
    {

        System.String str = "inital value";
        str = "\nsecond value";
        str = "\nthird value";

        StringBuilder sb = new StringBuilder();
        sb.Append("initial value");
        sb.AppendLine("second value");
        sb.AppendLine("third value");
    }
}

Génère MSIL suivant: Si vous examinez le code. Vous verrez que chaque fois que vous chane un objet de System.String, vous en créez un nouveau. Mais dans System.Text.StringBuilder chaque fois que vous modifiez la valeur du texte, vous ne recréez pas l'objet.

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       62 (0x3e)
  .maxstack  2
  .locals init ([0] string str,
           [1] class [mscorlib]System.Text.StringBuilder sb)
  IL_0000:  nop
  IL_0001:  ldstr      "inital value"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "\nsecond value"
  IL_000c:  stloc.0
  IL_000d:  ldstr      "\nthird value"
  IL_0012:  stloc.0
  IL_0013:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0018:  stloc.1
  IL_0019:  ldloc.1
  IL_001a:  ldstr      "initial value"
  IL_001f:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0024:  pop
  IL_0025:  ldloc.1
  IL_0026:  ldstr      "second value"
  IL_002b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_0030:  pop
  IL_0031:  ldloc.1
  IL_0032:  ldstr      "third value"
  IL_0037:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_003c:  pop
  IL_003d:  ret
} // end of method Program::Main
cahit beyaz
la source
5
Ummm ... ce code désassemblé montre seulement que les affectations de pointeurs sont en cours et que les méthodes sont appelées. Cela n'a (presque) rien à voir avec mutable versus immuable. (Cela m'échappe de savoir pourquoi certaines personnes pensent que le démontage d'un exemple de code non pertinent va aider les gens à comprendre ce genre de chose. Pour commencer, la plupart des programmeurs ne maîtrisent pas couramment le code CLI.)
Stephen C
6

Les chaînes sont modifiables car .NET utilise un pool de chaînes derrière la scène. Ça veut dire :

string name = "My Country";
string name2 = "My Country";

Le nom et le nom2 font référence au même emplacement mémoire à partir du pool de chaînes. Supposons maintenant que vous vouliez changer name2 en:

name2 = "My Loving Country";

Il recherchera dans le pool de chaînes la chaîne "My Loving Country", si elle est trouvée, vous en obtiendrez la référence, sinon une nouvelle chaîne "My Loving Country" sera créée dans la chaîne pool et name2 en obtiendra la référence. Mais tout ce processus " My Country " n'a pas été changé car une autre variable comme name l' utilise toujours. Et c'est la raison pour laquelle les chaînes sont IMMUTABLES .

StringBuilder fonctionne de manière différente et n'utilise pas de pool de chaînes. Lorsque nous créons une instance de StringBuilder:

var address  = new StringBuilder(500);

Il alloue un morceau de mémoire de taille 500 octets pour cette instance et toutes les opérations modifient simplement cet emplacement mémoire et cette mémoire non partagée avec un autre objet. Et c'est la raison pour laquelle StringBuilder est MUTABLE .

J'espère que cela aidera.

Rahul Garg
la source
5

Aucun, en fait. La classe String est modifiable.

unsafe
{
    string foo = string.Copy("I am immutable.");
    fixed (char* pChar = foo)
    {
        char* pFoo = pChar;

        pFoo[5] = ' ';
        pFoo[6] = ' ';
    }

    Console.WriteLine(foo); // "I am   mutable."
}

Ce type de logique est fait tout le temps dans les classes String et StringBuilder, en fait. Ils allouent simplement une nouvelle chaîne à chaque fois que vous appelez Concat, Substring, etc. et utilisent l'arithmétique du pointeur pour copier dans la nouvelle chaîne. Les cordes ne mutent tout simplement pas, d'où la raison pour laquelle elles sont considérées comme "immuables".


À propos, n'essayez pas cela avec des chaînes littérales ou vous risquez de gâcher votre programme:

string bar = "I am a string.";

fixed (char* pChar = bar)
{
    char* pBar = pChar;

    pBar[2] = ' ';
}

string baz = "I am a string.";

Console.WriteLine(baz); // "I  m a string."

Cela est dû au fait que les littéraux de chaîne sont internés dans le .NET Framework de bureau; en d'autres termes, baret bazpointez exactement sur la même chaîne, de sorte que la mutation de l'une mute l'autre. C'est très bien si vous utilisez une plate-forme non gérée comme WinRT, qui manque d'internement de chaînes.

James Ko
la source
1
Hé bien oui. Si vous enfreignez les règles en déchiffrant des abstractions ouvertes qui sont censées être fermées, alors presque rien n'est immuable. Vous pouvez faire des choses analogues en Java en utilisant une mauvaise réflexion ... mais le JLS déclare que le comportement que vous obtenez n'est pas défini.
Stephen C
2

Pour clarifier, il n'y a pas de chaîne mutable en C # (ou .NET en général). D'autres langues prennent en charge les chaînes mutables (chaîne qui peut changer) mais pas le framework .NET.

Donc, la bonne réponse à votre question est que TOUTES les chaînes sont immuables en C #.

string a une signification spécifique. Le mot-clé minuscule "string" est simplement un raccourci vers un objet instancié à partir de la classe System.String. Tous les objets créés à partir de la classe de chaînes sont TOUJOURS immuables.

Si vous voulez une représentation mutable du texte, vous devez utiliser une autre classe comme StringBuilder. StringBuilder vous permet de créer de manière itérative une collection de «mots», puis de la convertir en chaîne (encore une fois immuable).

Gerald Davis
la source
Désolé, mais la documentation Microsoft pour StringBuildercontredit ceci: voir msdn.microsoft.com/en-us/library
Stephen C
1

La valeur des données ne peut pas être modifiée. Remarque: la valeur de la variable peut être modifiée, mais la valeur de données immuable d'origine a été supprimée et une nouvelle valeur de données a été créée en mémoire.

Vikas Kumar
la source
1

dans le détail de la mise en œuvre.

System.String de CLR2 est mutable. StringBuilder.Append appelant String.AppendInplace (méthode privée)

System.String de CLR4 est immuable. StringBuilder a un tableau Char avec segmentation.

Kazuk
la source
0

De http://yassershaikh.com/what-is-the-difference-between-strings-and-stringbuilder-in-c-net/

Réponse courte: String est immuable - alors que StringBuilder est mutable.

Qu'est-ce que ça veut dire ? Wiki dit: En orienté objet, un objet immuable est un objet dont l'état ne peut pas être modifié après sa création. Cela contraste avec un objet mutable, qui peut être modifié après sa création.

À partir de la documentation de la classe StringBuilder:

L'objet String est immuable. Chaque fois que vous utilisez l'une des méthodes de la classe System.String, vous créez un nouvel objet chaîne en mémoire, ce qui nécessite une nouvelle allocation d'espace pour ce nouvel objet.

Dans les situations où vous devez effectuer des modifications répétées sur une chaîne, la surcharge associée à la création d'un nouvel objet String peut être coûteuse.

La classe System.Text.StringBuilder peut être utilisée lorsque vous souhaitez modifier une chaîne sans créer un nouvel objet. Par exemple, l'utilisation de la classe StringBuilder peut améliorer les performances lors de la concaténation de plusieurs chaînes dans une boucle.

Yasser Shaikh
la source
0

Voici l'exemple du générateur de chaîne immuable et de chaîne mutable

        Console.WriteLine("Mutable String Builder");
        Console.WriteLine("....................................");
        Console.WriteLine();
        StringBuilder sb = new StringBuilder("Very Good Morning");
        Console.WriteLine(sb.ToString());
        sb.Remove(0, 5);
        Console.WriteLine(sb.ToString());

        Console.WriteLine();

        Console.WriteLine("Immutable String");
        Console.WriteLine("....................................");
        Console.WriteLine();
        string s = "Very Good Morning";
        Console.WriteLine(s);
        s.Substring(0, 5);
        Console.WriteLine(s);
        Console.ReadLine();
Gokul
la source
Ce sera très bien si vous pouvez également fournir la sortie @Gokul :)
Roy Lee
La sous-chaîne ne modifie pas la valeur sous-jacente. Il ne renvoie qu'une valeur.
Kye
@Kye et pourquoi? parce que les chaînes sont immuables :)
LuckyLikey
Votre code à 2 lignes -: s.Substring (0, 5); Console.WriteLine (s); Ici, s.substring () ne change pas s. cet exemple ne montre pas si la chaîne est immuable.
Dhananjay
0

La chaîne en C # est immuable. Si vous la concaténez avec n'importe quelle chaîne, vous créez en fait une nouvelle chaîne, c'est-à-dire un nouvel objet chaîne! Mais StringBuilder crée une chaîne mutable.

Md Shahriar
la source
0

StringBuilder est une meilleure option pour concater une énorme chaîne de données, car StringBuilder est un type de chaîne mutable et l'objet StringBuilder est un type immuable, ce qui signifie que StringBuilder ne crée jamais une nouvelle instance d'objet tout en concatant la chaîne.

Si nous utilisons string au lieu de StringBuilder pour réaliser la concaténation, il créera une nouvelle instance en mémoire à chaque fois.

Sher Singh
la source