web-dev-qa-db-fra.com

Liste passée par réf - aidez-moi à expliquer ce comportement

Regardez le 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;
    }
}

J'ai supposé que myList serait passé par ref, et le résultat serait

3
4

La liste est effectivement "passée par ref", mais seule la fonction sort prend effet. La déclaration suivante myList = myList2; N'a aucun effet.

Donc, le résultat est en fait:

10
50
100

Pouvez-vous m'aider à expliquer ce comportement? Si en effet myList n'est pas passé-par-ref (tel qu'il apparaît dans myList = myList2 Ne prenant pas effet), comment myList.Sort() prend-t-il effet?

Je supposais même que cette déclaration ne prenait pas effet et que le résultat était:

100
50
10
94
nmdr

Vous passez une référence à la liste , mais vos ne le sont pas transmettent la variable de liste par référence - Ainsi, lorsque vous appelez ChangeList la valeur de la variable (c'est-à-dire le reference - think "pointeur") est copié - et passe à la valeur du paramètre dans ChangeList ne sont pas vus par TestMethod.

essayer:

private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);

Ceci passe ensuite une référence à la variable locale myRef (telle que déclarée dans TestMethod); maintenant, si vous réaffectez le paramètre dans ChangeList, vous réaffectez également la variable dans TestMethod.

99
Marc Gravell

Initialement, il peut être représenté graphiquement comme suit:

Init states

Ensuite, le tri est appliqué myList.Sort(); Sort collection

Enfin, quand 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.

Lost reference

Si vous utilisez par référence (ref), alors myList' et myList deviendront identiques (une seule référence).

Note: J'utilise myList' pour représenter le paramètre que vous utilisez dans ChangeList (parce que vous avez donné le même nom que l'original)

199
Jaider

Voici un moyen facile 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 transmettez jamais d'objets, vous transmettez leurs références par valeur.

  • Lorsque vous accédez à l'objet 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 méthode ChangeList est effectuée sur la valeur de la référence. Par conséquent, aucune modification n'est apportée à la liste d'origine (toujours sur le tas mais non référencé sur la variable de méthode).

18
Unmesh Kondolikar

this le lien vous aidera à comprendre passe par référence en C #. Fondamentalement, lorsqu'un objet de type référence est passé 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 () modifie 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 affecter un autre objet à la même variable, qui modifie l'objet entier lui-même.

9
Shekhar

C # ne fait qu'une copie superficielle quand il passe par valeur, à moins que l'objet en question n'exécute ICloneable (contrairement à la classe List).

Cela signifie qu'il copie le List lui-même, mais que les références aux objets de la liste restent les mêmes. c'est-à-dire que les pointeurs continuent à référencer les mêmes objets que l'original List.

Si vous modifiez les valeurs des éléments référencés par votre nouvelle référence List, vous modifiez également le paramètre List d'origine (car il fait référence aux mêmes objets). Cependant, vous modifiez ensuite entièrement ce que myList fait référence, en un nouveau List, et seul le texte original List fait désormais référence à ces entiers.

Lisez la section Passing Reference-Type Parameters de cet article MSDN sur les "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.

5
Ethel Evans

Bien que je suis 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 modifiez la signature de ChangeList (List myList) en une liste privée ChangeList (), vous verrez la sortie de 3, 4.

Voici mon raisonnement ... Même si liste est passée par référence, considérez cela comme transmettant une variable pointeur par valeur. Lorsque vous appelez ChangeList (myList), vous passez le pointeur à (Global) myList. Maintenant, cela est stocké dans la variable myList (locale). Alors maintenant, votre liste (locale) et ma liste (globale) pointent vers la même liste. Maintenant, vous effectuez un tri => cela fonctionne car maListe (locale) fait référence à la MaListe originale (globale). Vous créez ensuite une nouvelle liste et assignez le pointeur à votre liste MaListe (locale). Mais dès que la fonction quitte la variable (locale) maListe est détruite. HTH

class Test
{
    List<int> myList = new List<int>();
    public void TestMethod()
    {

        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList();

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList()
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}
3
sandeep

Utilisez le mot clé ref.

Regardez la référence définitive ici pour comprendre les paramètres de passage.
Pour être précis, regardez this , pour comprendre le comportement du code.

EDIT: Sort fonctionne sur la même référence (passée par valeur) et les valeurs sont donc ordonnées. Cependant, l'attribution d'une nouvelle instance au paramètre ne fonctionnera pas car le paramètre est passé par valeur, à moins que vous ne mettiez ref.

Mettre ref vous permet de remplacer le pointeur par la référence à une nouvelle instance de List dans votre cas. Sans ref, vous pouvez travailler sur le paramètre existant, mais vous ne pouvez pas le faire pointer sur autre chose.

2
shahkalpesh