web-dev-qa-db-fra.com

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

Le résultat est "Bar", ce qui signifie que l'objet a été passé en tant que référence.

261
Ryan

Passez un ref si vous voulez changer 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 à un objet complètement différent.

Cela peut également être utile si vous souhaitez modifier la valeur d'un objet immuable, par exemple. 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 en une autre avec une valeur différente.

Edit: Comme d'autres personnes l'ont mentionné. Ce n'est pas une bonne idée d'utiliser ref sauf si cela est nécessaire. Utiliser 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 même, 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 faire les choses décrites dans l'exemple ci-dessus. Toutefois, lorsque le type de paramètre est une valeur primitive (par exemple, int), si ce paramètre est affecté à la méthode, la valeur de l'argument transmis est 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;
}
272
Scott Langham

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 avec soin chaque fois que cela apparaît dans les groupes de discussion :)

83
Jon Skeet

Dans .NET, lorsque vous transmettez un paramètre à une méthode, une copie est créée. Dans les types de valeur, cela signifie que toute modification apportée à la valeur se situe dans l'étendue de la méthode et est perdue lorsque vous quittez la méthode.

Lors de la transmission d'un type de référence, une copie est également créée, mais il s'agit d'une copie d'une référence, c'est-à-dire que vous avez maintenant DEUX références en mémoire pour le même objet. Donc, si vous utilisez la référence pour modifier l'objet, il sera modifié. Mais si vous modifiez la référence elle-même (nous devons nous rappeler que c'est une copie), toutes les modifications sont également perdues à la sortie de la méthode.

Comme on l'a déjà dit, une assignation est une modification de la référence, elle est donc perdue:

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

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

Les méthodes ci-dessus ne modifient 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; }
        }
    }
47
Ricardo Amores

Puisque TestRef est une classe (qui sont des objets de référence), vous pouvez modifier le contenu à l'intérieur de t sans le transmettre en tant que réf. Cependant, si vous passez t en tant que ref, TestRef peut changer le nom du t original. c'est-à-dire qu'il pointe vers un objet différent.

17
Ferruccio

Avec ref vous pouvez écrire:

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

Et t sera changé une fois la méthode terminée.

15
Rinat Abdullin

Pensez aux variables (par exemple, foo) des types de référence (par exemple, List<T>) en tant que holding identificateurs d'objet de la forme "Objet # 24601". Supposons que l'instruction foo = new List<int> {1,5,7,9}; force foo à contenir "Object # 24601" (une liste de quatre éléments). Ensuite, l'appel de foo.Length demandera sa longueur à l'objet n ° 24601, qui répondra 4, ainsi foo.Length sera égal à 4.

Si foo est transmis à une méthode sans utiliser ref, cette méthode peut modifier l'objet n ° 24601. En conséquence de ces modifications, il est possible que foo.Length ne soit plus égal à 4. Cependant, la méthode elle-même ne pourra pas changer foo, qui conservera toujours "Objet # 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 également à foo lui-même. La méthode peut créer un nouvel objet # 8675309 et stocker une référence à celui-ci dans foo. Dans ce cas, foo ne contiendrait plus d'objet "24601", mais "d'objet 8675309".

En pratique, les variables de type référence ne contiennent pas de chaînes de la forme "Objet # 8675309"; ils ne tiennent même pas quelque chose qui puisse être converti de manière significative en un nombre. Bien que chaque variable de type référence contienne un modèle de bits, il n'y a pas de relation fixe entre les modèles de bits stockés dans ces variables et les objets qu'elles identifient. Il n’existe aucun moyen permettant à un code d’extraire des informations d’un objet ou une référence à celui-ci et de déterminer ultérieurement si une autre référence identifie le même objet, à moins que le code ne contienne ou ne connaisse une référence qui identifie l’objet original.

8
supercat

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

5

ref imite (ou se comporte) comme une zone globale pour seulement deux domaines:

  • Votre interlocuteur
  • Calleé.
3
guneysus

En utilisant le mot clé ref avec des types de référence, vous passez effectivement une référence à la référence. À bien des égards, cela revient à utiliser le mot clé out, à la différence près que rien ne garantit que la méthode affectera réellement quoi que ce soit au paramètre ref 'ed.

3
Isak Savo

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 transmettre un entier à une méthode, par exemple, et de lui demander de modifier l'entier en votre nom.

1
Andrew

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

Passer par référence n'est pas lié à une langue; c'est une stratégie de liaison de paramètre à côté de passer par valeur, passer par nom, passer par besoin, etc.

Sidenote: le nom de la classe TestRef est un choix terriblement mauvais dans ce contexte;).

1
xtofl