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?
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. string
s sont de type référence. Cela signifie que les variables de type string
s 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
, seulvalueA
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, string
s 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.
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.
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é.