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.
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;
}
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:)
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; }
}
}
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.
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.
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.
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.
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!
ref
imite (ou se comporte) comme une zone globale juste pour deux étendues:
- Appelant
- appelé.
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.
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 ;).