Je sais que "string" en C # est un type de référence. C'est sur MSDN. Cependant, ce code ne fonctionne pas comme il se doit alors:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
La sortie doit être "avant de passer" "après avoir passé" puisque je passe la chaîne en tant que paramètre et qu'il s'agit d'un type de référence, la deuxième instruction de sortie doit reconnaître que le texte a changé dans la méthode TestI. Cependant, j'obtiens "avant de passer" "avant de passer" faisant croire qu'il est passé par valeur et non par réf. Je comprends que les chaînes sont immuables, mais je ne vois pas comment cela expliquerait ce qui se passe ici. Qu'est-ce que je rate? Merci.
Réponses:
La référence à la chaîne est passée par valeur. Il y a une grande différence entre passer une référence par valeur et passer un objet par référence. Il est malheureux que le mot «référence» soit utilisé dans les deux cas.
Si vous faites passer la référence de chaîne par référence, il fonctionnera comme prévu:
Vous devez maintenant faire la distinction entre apporter des modifications à l'objet auquel une référence fait référence et apporter une modification à une variable (telle qu'un paramètre) pour la laisser faire référence à un objet différent. Nous ne pouvons pas apporter de modifications à une chaîne car les chaînes sont immuables, mais nous pouvons le démontrer avec un à la
StringBuilder
place:Voir mon article sur le passage de paramètres pour plus de détails.
la source
referenced
cela comme réponseSi nous devons répondre à la question: String est un type de référence et se comporte comme une référence. Nous transmettons un paramètre qui contient une référence à, pas la chaîne réelle. Le problème est dans la fonction:
Le paramètre
test
contient une référence à la chaîne, mais il s'agit d'une copie. Nous avons deux variables pointant vers la chaîne. Et comme toute opération avec des chaînes crée en fait un nouvel objet, nous faisons notre copie locale pour qu'elle pointe vers la nouvelle chaîne. Mais latest
variable d' origine n'est pas modifiée.Les solutions suggérées à mettre
ref
dans la déclaration de fonction et dans l'invocation fonctionnent car nous ne passerons pas la valeur de latest
variable mais nous lui passerons juste une référence. Ainsi, tout changement à l'intérieur de la fonction reflétera la variable d'origine.Je veux répéter à la fin: String est un type de référence, mais comme son immuable, la ligne
test = "after passing";
crée en fait un nouvel objet et notre copie de la variabletest
est modifiée pour pointer vers la nouvelle chaîne.la source
Comme d'autres l'ont indiqué, le
String
type dans .NET est immuable et sa référence est passée par valeur.Dans le code d'origine, dès que cette ligne s'exécute:
alors
test
ne fait plus référence à l'objet original. Nous avons créé un nouvelString
objet et assignétest
à référencer cet objet sur le tas géré.Je sens que beaucoup de gens se font trébucher ici car il n'y a pas de constructeur formel visible pour leur rappeler. Dans ce cas, cela se passe dans les coulisses car le
String
type prend en charge le langage dans la façon dont il est construit.C'est pourquoi le changement de
test
n'est pas visible en dehors de la portée de laTestI(string)
méthode - nous avons passé la référence par valeur et maintenant cette valeur a changé! Mais si laString
référence a été passée par référence, alors lorsque la référence a changé, nous la verrons en dehors de la portée de laTestI(string)
méthode.Le mot clé ref ou out est nécessaire dans ce cas. Je pense que le
out
mot-clé pourrait être légèrement mieux adapté à cette situation particulière.la source
out
doit être affecté dans la méthode pour que le code soit compilé.ref
n'a pas une telle exigence. Lesout
paramètres sont également initialisés en dehors de la méthode - le code de cette réponse est un contre-exemple.out
paramètres peuvent être initialisés en dehors de la méthode, mais ce n'est pas obligatoire. Dans ce cas, nous souhaitons initialiser leout
paramètre pour illustrer un point sur la nature dustring
type dans .NET.En fait, cela aurait été la même chose pour n'importe quel objet, c'est-à-dire qu'être un type de référence et passer par référence sont 2 choses différentes en c #.
Cela fonctionnerait, mais cela s'applique quel que soit le type:
Aussi à propos de la chaîne étant un type de référence, c'est aussi un type spécial. Il est conçu pour être immuable, donc toutes ses méthodes ne modifieront pas l'instance (elles en renvoient une nouvelle). Il contient également des éléments supplémentaires pour la performance.
la source
Voici un bon moyen de réfléchir à la différence entre les types de valeur, le passage par valeur, les types de référence et le passage par référence:
Une variable est un conteneur.
Une variable de type valeur contient une instance. Une variable de type référence contient un pointeur vers une instance stockée ailleurs.
La modification d'une variable de type valeur mute l'instance qu'elle contient. La modification d'une variable de type référence mute l'instance vers laquelle elle pointe.
Des variables de type référence distinctes peuvent pointer vers la même instance. Par conséquent, la même instance peut être mutée via n'importe quelle variable qui pointe vers elle.
Un argument passé par valeur est un nouveau conteneur avec une nouvelle copie du contenu. Un argument passé par référence est le conteneur d'origine avec son contenu d'origine.
Lorsqu'un argument de type valeur est passé par valeur: la réattribution du contenu de l'argument n'a aucun effet en dehors de la portée, car le conteneur est unique. La modification de l'argument n'a aucun effet en dehors de la portée, car l'instance est une copie indépendante.
Lorsqu'un argument de type référence est passé par valeur: la réattribution du contenu de l'argument n'a aucun effet en dehors de la portée, car le conteneur est unique. La modification du contenu de l'argument affecte la portée externe, car le pointeur copié pointe vers une instance partagée.
Lorsqu'un argument est passé par référence: la réattribution du contenu de l'argument affecte la portée externe, car le conteneur est partagé. La modification du contenu de l'argument affecte la portée externe, car le contenu est partagé.
En conclusion:
Une variable chaîne est une variable de type référence. Par conséquent, il contient un pointeur vers une instance stockée ailleurs. Lorsqu'il est passé par valeur, son pointeur est copié, donc la modification d'un argument de chaîne devrait affecter l'instance partagée. Cependant, une instance de chaîne n'a pas de propriétés mutables, donc un argument de chaîne ne peut pas être modifié de toute façon. Lorsqu'il est passé par référence, le conteneur du pointeur est partagé, donc la réaffectation affectera toujours la portée externe.
la source
" Une image vaut mille mots ".
J'ai un exemple simple ici, il est similaire à votre cas.
C'est ce qui s'est passé:
s1
et less2
variables font référence au même"abc"
objet string."abc"
objet chaîne ne se modifie pas lui-même (à"def"
), mais un nouvel"def"
objet chaîne est créé à la place et y faits1
référence.s2
toujours référence à l'"abc"
objet string, c'est donc la sortie.la source
Les réponses ci-dessus sont utiles, je voudrais simplement ajouter un exemple qui, à mon avis, montre clairement ce qui se passe lorsque nous transmettons un paramètre sans le mot clé ref, même lorsque ce paramètre est un type de référence:
la source
Pour les esprits curieux et pour compléter la conversation: Oui, String est un type de référence :
Mais notez que ce changement ne fonctionne que dans un bloc non sécurisé ! car les chaînes sont immuables (à partir de MSDN):
Et gardez à l'esprit que:
la source
Je pense que votre code est analogue à ce qui suit, et vous n'auriez pas dû vous attendre à ce que la valeur ait changé pour la même raison qu'elle ne le ferait pas ici:
la source
Essayer:
la source