Question

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.

Était-ce utile?

La solution

Passez un ref si vous souhaitez modifier 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 se réfère à un objet complètement différent.

Cela peut également être utile si vous souhaitez modifier la valeur d'un objet immuable, par exemple. un string. Vous ne pouvez pas changer la valeur d'un int une fois qu'il a été créé. Mais en utilisant un <=>, vous pouvez créer une fonction qui change la chaîne en une autre ayant une valeur différente.

Modifier: comme d'autres l'ont mentionné. Ce n'est pas une bonne idée d'utiliser <=> à moins que cela ne soit nécessaire. L'utilisation de <=> 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é <=> 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, <=>), 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;
}

Autres conseils

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

.

J'ai écrit un article assez long sur le sujet pour éviter de devoir les écrire soigneusement le temps que cela arrive sur les groupes de discussion:)

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

Puisque TestRef est une classe (qui sont des objets de référence), vous pouvez changer le contenu à l'intérieur de t sans le transmettre en tant que ref. 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.

Avec ref vous pouvez écrire:

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

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

Pensez aux variables (par exemple, foo) des types de référence (par exemple, List<T>) qui contiennent identificateurs d'objet de la forme " Objet # 24601 & <;>. Supposons que la déclaration foo = new List<int> {1,5,7,9}; force foo.Length à retenir & Quot; Object # 24601 & Quot; (une liste avec quatre éléments). Lorsque vous appelez ref, la longueur de l'objet # 24601 sera demandée et il répondra 4, donc <=> sera égal à 4.

Si <=> est passé à une méthode sans utiliser <=>, cette méthode peut apporter des modifications à l'objet # 24601. En conséquence de ces modifications, <=> pourrait ne plus être égal à 4. Cependant, la méthode elle-même ne pourra pas changer <=>, ce qui continuera à conserver & "Objet # 24601 &";

Passer <=> en tant que paramètre <=> permettra à la méthode appelée d’apporter des modifications non seulement à l’objet # 24601, mais également à <=> lui-même. La méthode peut créer un nouvel objet # 8675309 et stocker une référence à celui-ci dans <=>. Si tel est le cas, <=> ne tiendra plus & "Objet # 24601 &"; Mais à la place de & "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 initial.

Cela revient à faire passer un pointeur sur un pointeur en C. Dans .NET, cela vous permettra de changer la signification du T d'origine, personnellement , mais je pense que si vous le faites en .NET vous avez probablement un problème de conception!

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, 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 <=> 'ed.

ref imite (ou se comporte) comme une zone globale pour seulement deux portées:

  • Appelant
  • Appelé.

Cependant, si vous transmettez une valeur, les choses sont différentes. Vous pouvez forcer une valeur à être transmise par référence. Cela vous permet, par exemple, de passer un entier à une méthode et de la modifier en votre nom.

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 passe par valeur, passe par nom, passe par besoin, etc ...

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top