Jetez un œil au programme suivant:
class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);
ChangeList(myList);
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
private void ChangeList(List<int> myList)
{
myList.Sort();
List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);
myList = myList2;
}
}
Je supposais que myList
serait passé ref
, et la sortie serait
3
4
La liste est bien "passée par ref", mais seule la sort
fonction prend effet. La déclaration suivante myList = myList2;
n'a aucun effet.
Donc, la sortie est en fait:
10
50
100
Pouvez-vous m'aider à expliquer ce comportement? Si en effet myList
n'est pas passé par référence (comme il semble myList = myList2
ne pas avoir d'effet), comment myList.Sort()
prend-il effet?
Je supposais même que cette déclaration ne prenait pas effet et que la sortie était:
100
50
10
c#
list
pass-by-reference
nmdr
la source
la source
ChangeList
de renvoyer unList<int>
plutôt que d'être unvoid
s'il s'agit en fait de créer une nouvelle liste.Réponses:
Vous passez une référence à la liste , mais vous ne transmettez pas la variable de liste par référence - donc lorsque vous appelez
ChangeList
la valeur de la variable (c'est-à-dire la référence - pensez "pointeur") est copiée - et change la valeur du les paramètres à l' intérieurChangeList
ne sont pas vus parTestMethod
.essayer:
Cela passe ensuite une référence à la variable locale
myRef
(comme déclaré dansTestMethod
); maintenant, si vous réaffectez le paramètre à l'intérieur,ChangeList
vous réaffectez également la variable à l' intérieurTestMethod
.la source
ChangeList
, seule la référence est copiée - c'est le même objet. Si vous modifiez l'objet d'une manière ou d'une autre, tout ce qui a une référence à cet objet verra le changement.Initialement, il peut être représenté graphiquement comme suit:
Ensuite, le tri est appliqué
myList.Sort();
Enfin, lorsque vous avez fait:,
myList' = myList2
vous avez perdu celui de la référence mais pas l'original et la collection est restée triée.Si vous utilisez par référence (
ref
) alorsmyList'
etmyList
deviendra le même (une seule référence).Remarque: j'utilise
myList'
pour représenter le paramètre que vous utilisez dansChangeList
(car vous avez donné le même nom que l'original)la source
Voici un moyen simple de le comprendre
Votre liste est un objet créé sur le tas. La variable
myList
est une référence à cet objet.En C #, vous ne passez jamais d'objets, vous passez leurs références par valeur.
Lorsque vous accédez à l'objet de liste via la référence passée dans
ChangeList
(lors du tri, par exemple), la liste d'origine est modifiée.L'affectation sur la
ChangeList
méthode est faite à la valeur de la référence, donc aucune modification n'est apportée à la liste d'origine (toujours sur le tas mais plus référencée sur la variable de méthode).la source
Ce lien vous aidera à comprendre le passage par référence en C #. Fondamentalement, lorsqu'un objet de type référence est passé par valeur à une méthode, seules les méthodes disponibles sur cet objet peuvent modifier le contenu de l'objet.
Par exemple, la méthode List.sort () change le contenu de la liste mais si vous affectez un autre objet à la même variable, cette affectation est locale à cette méthode. C'est pourquoi myList reste inchangé.
Si nous transmettons un objet de type référence en utilisant le mot-clé ref, nous pouvons attribuer un autre objet à la même variable et cela change tout l'objet lui-même.
(Modifier: il s'agit de la version mise à jour de la documentation liée ci-dessus.)
la source
C # fait juste une copie superficielle quand il passe par valeur à moins que l'objet en question ne s'exécute
ICloneable
(ce que laList
classe ne fait apparemment pas).Cela signifie qu'il copie
List
lui - même, mais les références aux objets à l'intérieur de la liste restent les mêmes; autrement dit, les pointeurs continuent à référencer les mêmes objets que l'originalList
.Si vous modifiez les valeurs des choses que vos nouvelles
List
références, vous modifiezList
également l'original (puisqu'il fait référence aux mêmes objets). Cependant, vous modifiez ensuite ce qui faitmyList
référence entièrement à un nouveauList
, et maintenant seul l'originalList
fait référence à ces entiers.Lisez la section Passing Reference-Type Parameters de cet article MSDN sur «Passing Parameters» pour plus d'informations.
"Comment cloner une liste générique en C #" de StackOverflow explique comment créer une copie complète d'une liste.
la source
Bien que je sois d'accord avec ce que tout le monde a dit ci-dessus. J'ai une vision différente de ce code. En gros, vous affectez la nouvelle liste à la variable locale myList et non à la variable globale. si vous changez la signature de ChangeList (List myList) en private void ChangeList (), vous verrez la sortie de 3, 4.
Voici mon raisonnement ... Même si la liste est passée par référence, pensez-y comme en passant une variable de pointeur par valeur Lorsque vous appelez ChangeList (myList), vous passez le pointeur à (Global) myList. Maintenant, cela est stocké dans la variable (locale) myList. Alors maintenant, votre (locale) myList et (globale) myList pointent vers la même liste. Maintenant, vous faites un tri => cela fonctionne car (local) myList fait référence à la myList d'origine (globale) Ensuite, vous créez une nouvelle liste et assignez le pointeur à votre (locale) myList. Mais dès que la fonction sort, la variable (locale) myList est détruite. HTH
la source
Utilisez le
ref
mot - clé.Regardez la référence définitive ici pour comprendre les paramètres de passage.
Pour être précis, regardez ceci , pour comprendre le comportement du code.
EDIT:
Sort
fonctionne sur la même référence (qui est passée par valeur) et donc les valeurs sont ordonnées. Cependant, l'affectation d'une nouvelle instance au paramètre ne fonctionnera pas car le paramètre est passé par valeur, sauf si vous mettezref
.Putting
ref
vous permet de changer le pointeur vers la référence à une nouvelle instance deList
dans votre cas. Sansref
, vous pouvez travailler sur le paramètre existant, mais vous ne pouvez pas le faire pointer vers autre chose.la source
Il y a deux parties de mémoire allouées pour un objet de type référence. Un en pile et un en tas. La partie dans la pile (aka un pointeur) contient une référence à la partie dans le tas - où les valeurs réelles sont stockées.
Lorsque le mot clé ref n'est pas utilisé, une seule copie de la pièce dans la pile est créée et transmise à la méthode - référence à la même pièce dans le tas. Par conséquent, si vous modifiez quelque chose en tas, ces changements seront conservés. Si vous modifiez le pointeur copié - en l'affectant pour qu'il fasse référence à un autre endroit dans le tas - cela n'affectera pas le pointeur d'origine en dehors de la méthode.
la source