Pourquoi utiliser le mot-clé' ref ' lors du passage d'un objet?

Si je passe un objet à une méthode, Pourquoi devrais-je utiliser le mot-clé ref? N'est-ce pas le comportement par défaut de toute façon?

Par exemple:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

La sortie est " Bar " ce qui signifie que l'objet a été passé comme référence.

226
demandé sur Jim Fell 2008-10-09 15:49:35

11 réponses

Passez un ref Si vous voulez changer ce qu'est l'objet:

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

Après avoir appelé DoSomething, t ne fait pas référence à L'original new TestRef, mais fait référence à un objet complètement différent.

Cela peut également être utile si vous voulez changer la valeur d'un objet immuable, par exemple un string. Vous ne pouvez pas modifier la valeur d'un string Une fois qu'il a été créé. Mais en utilisant un ref, Vous pouvez créer une fonction qui change la chaîne pour une autre qui a un autre valeur.

Edit: Comme d'autres personnes l'ont mentionné. Ce n'est pas une bonne idée d'utiliser ref sauf si c'est nécessaire. L'utilisation de ref donne à la méthode la liberté de changer l'argument pour autre chose, les appelants de la méthode devront être codés pour s'assurer qu'ils gèrent cette possibilité.

De plus, lorsque le type de paramètre est un objet, les variables d'objet agissent toujours comme des références à l'objet. Cela signifie que lorsque le mot clé ref est utilisé, vous avez une référence à une référence. Cela vous permet de vous pour faire les choses comme décrit dans l'exemple donné ci-dessus. Mais, lorsque le type de paramètre est une valeur primitive (par exemple int), alors si ce paramètre est assigné à dans la méthode, la valeur de l'argument qui a été passé sera modifiée après le retour de la méthode:

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}
239
répondu Scott Langham 2016-09-20 21:51:26

Vous devez faire la distinction entre "passer une référence par valeur" et "passer un paramètre / argument par référence".

J'ai écrit un article assez long sur le sujet pour éviter d'avoir à écrire soigneusement chaque fois que cela arrive sur les newsgroups:)

77
répondu Jon Skeet 2017-08-30 10:31:08

Dans. net lorsque vous passez un paramètre à une méthode, une copie est créée. Dans les types de valeur signifie que toute modification que vous apportez à la valeur est à la portée de la méthode et est perdue lorsque vous quittez la méthode.

Lors du passage d'un type de Référence, une copie est également faite, mais c'est une copie d'une référence, c'est-à-dire que vous avez maintenant deux références en mémoire au même objet. Donc, si vous utilisez la référence pour modifier l'objet, il est modifié. Mais si vous modifiez la référence elle-même - nous devons nous rappeler qu'il est un copy - alors toutes les modifications sont également perdues à la sortie de la méthode.

Comme les gens l'ont déjà dit, une affectation est une modification de la référence, donc est perdue:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

Les méthodes ci-dessus ne modifie pas l'objet d'origine.

Une petite modification de votre exemple

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }
30
répondu Ricardo Amores 2014-11-17 09:06:30

Puisque TestRef est une classe (qui sont des objets de référence), vous pouvez changer le contenu dans t sans le passer en tant que ref. Cependant, si vous passez t en tant que ref, TestRef peut changer à quoi se réfère le t original. c'est à dire faire pointer vers un objet différent.

17
répondu Ferruccio 2008-10-09 11:53:32

Avec ref, vous pouvez écrire:

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

Et t seront modifiés une fois la méthode terminée.

14
répondu Rinat Abdullin 2015-07-26 20:39:28

Pensez aux variables (par exemple foo) des types de référence (par exemple List<T>) comme contenant identificateurs d'objet de la forme "Object # 24601". Supposons que l'instruction foo = new List<int> {1,5,7,9}; provoque foo à contenir "Object #24601" (une liste avec quatre éléments). Alors appeler {[4] } demandera à L'objet # 24601 sa longueur, et il répondra 4, donc foo.Length sera égal à 4.

Si foo est passé à une méthode sans utiliser ref, cette méthode peut apporter des modifications à L'objet # 24601. En conséquence de ces changements, foo.Length pourrait ne plus égaler 4. La méthode elle-même, cependant, ne pourra pas changer foo, qui continuera à contenir "Object # 24601".

Passer foo en tant que paramètre ref permettra à la méthode appelée d'apporter des modifications non seulement à L'objet # 24601, mais aussi à foo lui-même. La méthode peut créer un nouvel objet # 8675309 et stocker une référence à cela dans foo. Si c'est le cas, foo ne contiendrait plus "Object # 24601", mais plutôt "Object # 8675309".

En pratique, Type de référence les variables ne contiennent pas de chaînes de la forme "Object #8675309"; elles ne contiennent même rien qui puisse être converti de manière significative en un nombre. Même si chaque variable de type référence contient un motif de bits, il n'y a pas de relation fixe entre les motifs de bits stockés dans ces variables et les objets qu'ils identifient. Il n'y a aucun moyen que le code puisse extraire des informations d'un objet ou d'une référence, et déterminer plus tard si une autre Référence a identifié le même objet, sauf si le code soit détenu ou connu d'une référence qui a identifié l'objet original.

6
répondu supercat 2015-07-17 16:30:51

En utilisant le mot clé ref avec les types de référence, vous passez effectivement une référence à la référence. À bien des égards, c'est la même chose que d'utiliser le mot-clé out, mais avec la différence mineure qu'il n'y a aucune garantie que la méthode assignera quoi que ce soit au paramètre ref'ed.

3
répondu Isak Savo 2008-10-09 11:53:39

C'est comme passer un pointeur à un pointeur dans C. Dans. net cela vous permettra de changer à quoi se réfère le T original, personnellement bien que je pense que si vous faites cela dans. NET, vous avez probablement un problème de conception!

3
répondu pezi_pink_squirrel 2008-10-09 11:58:03

ref imite (ou se comporte) comme une zone globale juste pour deux étendues:

  • Appelant
  • appelé.
3
répondu guneysus 2017-10-15 22:58:46

Si vous passez une valeur, cependant, les choses sont différentes. Vous pouvez forcer une valeur à être passée par référence. Cela vous permet de passer un entier à une méthode, par exemple, et que la méthode modifie l'entier en votre nom.

1
répondu Andrew 2008-10-09 11:51:31

Ref indique si la fonction peut mettre la main sur l'objet lui-même, ou seulement sur sa valeur.

Le passage par référence n'est pas lié à un langage; c'est une stratégie de liaison de paramètre à côté de pass-by-value, pass by name, pass by need etc...

A sidenote: le nom de classe TestRef est un choix hideusement mauvais dans ce contexte ;).

1
répondu xtofl 2008-10-09 11:58:07