web-dev-qa-db-fra.com

C # transmet-il un List <T> à une méthode par référence ou comme copie?

Faire mes premiers pas dans le monde C # à partir de C/C++, donc un peu flou dans les détails. Les cours, pour autant que je sache, sont passés par référence par défaut, mais qu'en est-il par exemple. Liste <chaîne> comme dans:

void DoStuff(List<string> strs)
{
    //do stuff with the list of strings
}

et ailleurs

List<string> sl = new List<string>();
//next fill list in a loop etc. and then do stuff with it:
DoStuff(sl);

Est sl dans ce cas passé par référence ou est une copie faite de sorte que je devrais redéfinir la fonction de travail comme

void DoStuff (ref List <string> strs)
sl
16
Scre

C'est passé par référence. List<T> est une classe et toutes les instances de classe sont passées par référence.

14

Le comportement est toujours le même: passer en copiant. Dans le cas où le paramètre est un objet, le référence de l'objet est copié, donc en fait vous travaillez sur le même objet/liste/peu importe.

9

La chose sous-jacente est toujours: les types de valeur sont passés par valeur et les types de référence sont "passés par référence" (cités parce que la valeur de la référence est réellement passée par valeur, mais la plupart des gens l'ignorent pour des raisons de concision).

La manière la plus simple de réconcilier le mot clé ref avec les références est: les types de référence ont leur référence passée par valeur. Cela a pour effet, dans le cas standard, de simplement passer la référence à la liste (et non la liste entière) à la méthode.

Le mot clé ref, lorsqu'il est utilisé sur un type de référence, transmet sémantiquement une référence à la référence (j'ai vraiment du mal à ne pas dire "pointeur vers un pointeur").

Si votre méthode devait réaffecter l'argument ref à un nouvel objet, l'appelant verrait également cette nouvelle affectation. Alors que sans le mot clé ref, la méthode serait simplement de réaffecter leur propre copie locale de la valeur de référence et l'appelant aurait toujours une référence à leur objet d'origine.

L'explication ci-dessus est sans vergogne tirée de article de Jon Skeet sur le sujet :

Cette différence est absolument cruciale pour comprendre le passage des paramètres en C #, et c'est pourquoi je pense qu'il est très déroutant de dire que les objets sont passés par référence par défaut au lieu de la déclaration correcte selon laquelle les références aux objets sont passées par valeur par défaut.

Le mot clé ref n'est nécessaire que si vous avez l'intention de réaffecter l'argument et de le rendre visible à l'appelant. Dans la plupart des cas, vous constaterez que ce n'est pas nécessaire. Votre DoStuff peut être réécrit pour le supprimer et toujours transmettre avec succès une référence à la liste par valeur:

void DoSomething(List<string> strs) 
{ 
    strs.Add("Hello");
}
4

En plus des autres réponses, il est très important de comprendre le comportement de ref

Voici un exemple de code à des fins de démonstration

static void Main(string[] args)
    {

        List<string> lstStr = new List<string>();

        lstStr.Add("First");
        lstStr.Add("Second");

        Alter(lstStr);

        //Alter(ref lstStr);

        Console.WriteLine("---From Main---");
        foreach (string s in lstStr)
        {
            Console.WriteLine(s);
        }

        Alter2(ref lstStr);

        Console.WriteLine("---From Main after passed by ref---");
        foreach (string s in lstStr)
        {
            Console.WriteLine(s);
        }

        Console.ReadKey();
    }

    static void Alter(List<string> lstStr2)
    {
        lstStr2.Add("Third");

        Console.WriteLine("----From Alter----");
        foreach (string s in lstStr2)
        {
            Console.WriteLine(s);
        }

        lstStr2 = new List<string>();
        lstStr2.Add("Something new");

        Console.WriteLine("----From Alter - after the local var is assigned somthing else----");

        foreach (string s in lstStr2)
        {
            Console.WriteLine(s);
        }

    }

    static void Alter2(ref List<string> lstStr2)
    {
        lstStr2 = new List<string>();
        lstStr2.Add("Something new from alter 2");

        Console.WriteLine("----From Alter2 - after the local var is assigned new list----");

        foreach (string s in lstStr2)
        {
            Console.WriteLine(s);
        }

    }


//----From Alter----
//First
//Second
//Third
//----From Alter - after the local var is assigned somthing else----
// Something new
// ---From Main---
// First
// Second
// Third
// ----From Alter2 - after the local var is assigned new list----
// Something new from alter 2
// ---From Main after passed by ref---
// Something new from alter 2
4
ZedBee

Le mot clé ref dans votre méthode est redondant si vous souhaitez modifier la liste d'origine: List<T> est un type de référence (class en C #) et sera donc transmis à la méthode par référence; par conséquent, la méthode manipulera la liste d'origine.

Lors du passage d'un Value Type, il créera une copie de la valeur elle-même. Lors du passage d'un Reference Type, il créera une copie de la référence.

En savoir plus sur Types de valeur et de référence en C #.

2
Yuval Itzchakov

La liste est transmise par référence. Cela signifie en fait que la variable strs à l'intérieur de la méthode fait référence à la même liste que la variable sl à l'extérieur de la méthode. Si vous utilisez ref, vous pouvez réellement réaffecter la variable sl à l'intérieur de la méthode.

strs = new List<string>()

ferait sl pointe vers la nouvelle liste.

Puisque vous venez de C/C++: ref pourrait être considéré comme un "pointeur sûr". C'est similaire à l'utilisation de & strs

2
Dennis_E