Ok, donc je suis un débutant en C # et j'ai appris le concept de Reference Type and Value Type
. J'ai également compris que Value type
est sur Stack
où les Reference types
sont stockés dans Heap
. Et puis nous devons allouer manuellement de la mémoire à Reference type
et tout ça. Mais je ne suis pas en mesure de comprendre ci-dessous le comportement. Guidez-moi s'il-vous-plaît.
Exemple 1:
var arr1 = new int[3] { 1, 2, 3 }; // memory allocated
var arr2 = arr1; // another variable created but pointing to same memory in Heap
arr2[0] = 2; // value in array 2 changed
System.Console.WriteLine("");
foreach (var item in arr1)
{
System.Console.WriteLine(item);
}
System.Console.WriteLine("------------");
foreach (var item in arr2)
{
System.Console.WriteLine(item);
}
Sortie comme:
arr1: 2,2,3
arr2: 2,2,3
Conclusion: puisque les deux pointaient vers le même emplacement mémoire. Ainsi, lorsque la valeur dans Array 2 est modifiée, la valeur dans Array1 est également affectée. Jusqu'ici tout va bien.
Il existe maintenant un autre type de référence qui est une chaîne.
Considérez ci-dessous l'exemple 2:
var Name = "Mosh";
var FName = Name;
FName = "Hello";
System.Console.WriteLine(Name);
System.Console.WriteLine(FName);
Sortie comme:
Mosh
Hello
Résultat attendu:
Hello
Hello
Depuis que j'ai changé la valeur de FName
, je m'attendais à ce que la valeur Name
soit également modifiée car les deux doivent pointer vers le même emplacement mémoire . C'est l'une des questions les plus simples sur SO, je suis un débutant alors soyez indulgents avec moi.
3 réponses
Votre confusion vient en partie du fait que vous voyez ces deux déclarations comme quelque peu équivalentes par nature. Ils ne sont pas!
arr2[0] = 2;
FName = "Hello";
Dans le premier cas, le tableau vers lequel pointe arr2
est modifié. Mais, la référence de arr2
à ce tableau reste inchangée. Ainsi, arr1
et arr2
continuent de pointer vers le même tableau et voient tous les deux les modifications apportées au tableau.
Dans le second cas cependant, ce n'est pas l'objet vers lequel pointe FName
qui est modifié. C'est-à-dire que nous ne modifions en aucun cas la chaîne "Mosh"
. Au contraire, nous changeons la référence de FName
pour pointer vers un emplacement mémoire différent où la chaîne immuable "Hello"
réside dans le tas. Après ce point, FName
ne pointe plus vers le même emplacement mémoire que Name
. Name
continue de pointer vers l'emplacement mémoire de "Mosh"
, qui reste inchangé.
Dans le .Net Framework, une chaîne est un type de référence immuable.
Étant donné qu'une chaîne n'a pas de taille de mémoire prédéfinie (comme les types de valeur qui peuvent être stockés dans la pile), elle peut devenir volumineuse (environ 2 milliards de caractères Unicode) et nécessite une allocation de mémoire dynamique. Lorsqu'un objet String est créé, la valeur réelle est stockée dans la mémoire dynamique, le Heap.
Immuable signifie qu'il ne peut pas être modifié après sa création. Chaque modification apportée à une chaîne créera une nouvelle chaîne. C'est pourquoi toutes les méthodes de manipulation String renvoient une chaîne.
Les types de référence ont des frais généraux sur la construction et la destruction et garbage collection, car ils sont créés sur le tas. Types de valeur d'autre part, ont une surcharge sur les appels de méthode (si la taille des données plus grand qu'un pointeur), car tout l'objet est copié plutôt que juste un pointeur. Parce que les chaînes peuvent être beaucoup plus grandes que la taille d'un pointeur, ils sont conçus comme des types de référence. Réf.
Dans votre cas: lorsque vous attribuez un nom à FName: FName = Name
les deux chaînes font référence au même objet (comme indiqué dans l'exemple de code) qui contient la chaîne "Mosh"
dans le tas. Une fois que FName
a été défini sur une autre valeur de chaîne, il référence un emplacement mémoire différent qui stocke la nouvelle chaîne "Hello"
. Le nom continue de pointer vers l'emplacement mémoire d'origine qui stocke "Mosh"
et reste inchangé.
Ce comportement de String en tant que type valeur est moins évident lorsqu'il est utilisé comme paramètre qui met à jour la valeur dans la fonction. Tant que le paramètre String n'est pas fourni par référence, sa valeur d'origine ne sera pas modifiée (voir l'exemple).
using System;
public class Program
{
public static void Main()
{
var Name = "Mosh";
var FName = Name;
Console.WriteLine(Object.ReferenceEquals(Name,FName));
FName = "Hello";
System.Console.WriteLine(Name);
System.Console.WriteLine(FName);
Console.WriteLine(Object.ReferenceEquals(Name,FName));
TestFunc(Name);
System.Console.WriteLine(Name);
RefFunc(ref FName);
System.Console.WriteLine(FName);
}
public static void TestFunc(string test)
{
test = "after passing";
}
public static void RefFunc(ref string test)
{
test = "after passing";
}
}
Une chaîne dans un type de référence oui, mais selon la conception de C #, elle se comportera plus ou moins comme les autres types de primitives / valeurs.
Pour obtenir l'effet dont vous avez parlé, vous devez spécifier qu'une valeur est un pointeur, quelque chose de similaire à: string* PName = Name;
Votre question, "Est-il prudent de dire qu'ils pointent vers le même emplacement en mémoire?" Je ne suis pas vraiment sûr, bien que je sache que les chaînes n'ont pas d'adresse fixe en mémoire, vous ne pouvez donc pas utiliser "&" ou "GetHashCode" comme vous pouvez le tester sur des entiers ou d'autres types.
Modifier 1:
Eh bien, je serai l'oncle d'un singe, tu as raison. Essaye ça:
Console.WriteLine(Object.ReferenceEquals(Name,FName)
De nouvelles questions
c#
C # (prononcé "see sharp") est un langage de programmation multi-paradigme de haut niveau, typé statiquement développé par Microsoft. Le code C # cible généralement la famille d'outils et d'exécutions Microsoft .NET, notamment le .NET Framework, .NET Core et Xamarin. Utilisez cette balise pour les questions sur le code écrit en C # ou en spécification formelle de C #.