web-dev-qa-db-fra.com

Affectation de chaîne en C #

Il y a quelques semaines, j'ai découvert que les chaînes en C # sont définies comme des types de référence et non des types de valeur. Au début, j'étais confus à ce sujet, mais après une lecture, j'ai soudainement compris pourquoi il était important de stocker des chaînes sur le tas et non sur la pile - car il serait très inefficace d'avoir une très grande chaîne qui est copiée sur un nombre imprévisible de cadres de pile. J'accepte complètement cela.

Je pense que ma compréhension est presque complète, mais il manque un élément: quelle fonctionnalité de langage les chaînes utilisent-elles pour les garder immuables? Pour illustrer avec un exemple de code:

string valueA = "FirstValue";
string valueB = valueA;
valueA = "AnotherValue";

Assert.AreEqual("FirstValue", valueB); // Passes

Je ne comprends pas quelle fonction de langue fait une copie de valueA lorsque je l'attribue à valueB. Ou peut-être, la référence à valueA ne change pas lorsque je l'affecte à valueB, seule valueA obtient une nouvelle référence à elle-même lorsque je définit la chaîne. Comme il s'agit d'un type d'instance, je ne comprends pas pourquoi cela fonctionne.

Je comprends que vous pouvez surcharger, par exemple, les opérateurs == et! =, Mais je n'arrive pas à trouver de documentation sur la surcharge des opérateurs =. Quelle est l'explication?

21
Steve Rukuts

quelle fonctionnalité de langage les chaînes utilisent-elles pour les garder immuables?

Ce n'est pas une fonction de langue. C'est la façon dont la classe est définie.

Par exemple,

class Integer {
    private readonly int value;

    public int Value { get { return this.value; } }
    public Integer(int value) { this.value = value; } }
    public Integer Add(Integer other) {
        return new Integer(this.value + other.value);
    }
}

est comme un int sauf que c'est un type de référence, mais il est immuable. Nous défini qu'il en soit ainsi. Nous pouvons également le définir comme étant mutable:

class MutableInteger {
    private int value;

    public int Value { get { return this.value; } }
    public MutableInteger(int value) { this.value = value; } }
    public MutableInteger Add(MutableInteger other) {
        this.value = this.value + other.value;
        return this;
    } 
}

Voir?

Je ne comprends pas quelle fonction de langue crée une copie de valueA lorsque je l'assigne à valueB.

Il ne copie pas le string, il copie la référence. strings sont de type référence. Cela signifie que les variables de type strings sont des emplacements de stockage dont les valeurs sont des références. Dans ce cas, leurs valeurs sont des références à des instances de string. Lorsque vous affectez une variable de type string à une autre de type string, la valeur est copiée. Dans ce cas, la valeur est une référence et elle est copiée par l'affectation. Cela est vrai pour tout type de référence, pas seulement string ou seulement les types de référence immuables.

Ou peut-être, la référence à valueA ne change pas lorsque je l'affecte à valueB, seul valueA obtient une nouvelle référence pour lui-même lorsque je mets la chaîne.

Non, les valeurs de valueA et valueB font référence à la même instance de string. Leurs valeurs sont des références et ces valeurs sont égales. Si vous pouviez en quelque sorte muter* l'instance de string référencée par valueA, le référent à la fois de valueA et valueB verrait cette mutation.

Comme il s'agit d'un type d'instance, je ne comprends pas pourquoi cela fonctionne.

Il n'existe pas de type d'instance.

Fondamentalement, strings sont des types de référence. Mais string sont immuables. Lorsque vous mutez un string, ce qui se passe est que vous obtenez une référence à une nouvelle chaîne qui est le résultat de la mutation vers le string déjà existant.

string s = "hello, world!";
string t = s;
string u = s.ToUpper();

Ici, s et t sont des variables dont les valeurs se réfèrent à la même instance de string. Le référent de s n'est pas muté par l'appel à String.ToUpper. Au lieu, s.ToUpper fait une mutation du référent de s et renvoie une référence à une nouvelle instance de string qu'il crée dans le processus d'application de la mutation. Nous attribuons cette référence à u.

Je comprends que vous pouvez surcharger, par exemple, les opérateurs == et! =, Mais je n'arrive pas à trouver de documentation sur la surcharge des opérateurs =.

Vous ne pouvez pas surcharger =.

* Vous pouvez, avec quelques astuces. Ignore les.

16
jason

Tout d'abord, votre exemple fonctionnera de la même manière pour toutes les variables de référence, pas seulement pour les chaînes.

Ce qui se produit est:

string valueA = "FirstValue"; //ValueA is referenced to "FirstValue"  
string valueB = valueA; //valueB references to what valueA is referenced to which is "FirstValue"  
valueA = "AnotherValue"; //valueA now references a new value: "AnotherValue"
Assert.AreEqual("FirstValue", valueB); // remember that valueB references "FirstValue"

Maintenant, l'immutabilité est un concept différent. Cela signifie que la valeur elle-même ne peut pas être modifiée.
Cela apparaîtra dans une situation comme celle-ci:

string valueA = "FirstValue"; //ValueA is referenced to "FirstValue"  
string valueB = valueA; //valueB references to what valueA is referenced to which is "FirstValue"  
valueA.Replace('F','B'); //valueA will now be: "BirstValue"
Assert.AreEqual("FirstValue", valueB); // remember that valueB references "FirstValue"

C'est à cause de l'immuabilité de String, valueA ne change pas la chaîne elle-même ... Il crée une nouvelle [~ # ~] copie [~ # ~] avec les modifications et les références qui.

6
Yochai Timmer

Ou peut-être, la référence à valueA ne change pas lorsque je l'affecte à valueB, seule valueA obtient une nouvelle référence à elle-même lorsque je mets la chaîne.

C'est exact. Comme les chaînes sont immuables, il n'y a aucun problème à ce que deux variables référencent le même objet chaîne. Lorsque vous affectez une nouvelle chaîne à l'un d'eux, c'est la référence qui est remplacée, pas l'objet chaîne.

Je n'arrive pas à trouver de documentation sur la surcharge des opérateurs =.

Ce n'est pas dû à une lacune de votre côté, c'est parce qu'il n'y a aucun moyen de surcharger l'opérateur d'affectation en C #.

L'opérateur = Est assez simple, il prend la valeur du côté droit et l'affecte à la variable du côté gauche. S'il s'agit d'un type de référence, la valeur est la référence, c'est donc ce qui est attribué.

3
Guffa