web-dev-qa-db-fra.com

À quoi sert "ref" pour les variables de type référence en C #?

Je comprends que si je passe un type de valeur (int, struct, etc.) comme paramètre (sans le mot clé ref), une copie de cette variable est transmise à la méthode, mais si j'utilise le mot clé ref, une référence à cette variable est passée, pas une nouvelle.

Mais avec des types de référence, comme des classes, même sans le mot clé ref, une référence est passée à la méthode, pas une copie. Alors, à quoi sert le mot-clé ref avec les types de référence?


Prends pour exemple:

var x = new Foo();

Quelle est la différence entre les éléments suivants?

void Bar(Foo y) {
    y.Name = "2";
}

et

void Bar(ref Foo y) {
    y.Name = "2";
}
164
Andreas Grech

Vous pouvez modifier ce que foo pointe en utilisant y:

Foo foo = new Foo("1");

void Bar(ref Foo y)
{
    y = new Foo("2");
}

Bar(ref foo);
// foo.Name == "2"
148
user7116

Il y a des cas où vous voulez modifier le --- reference réel et non l'objet pointé vers:

void Swap<T>(ref T x, ref T y) {
    T t = x;
    x = y;
    y = t;
}

var test = new[] { "0", "1" };
Swap(ref test[0], ref test[1]);
29
Mehrdad Afshari

Jon Skeet a écrit n excellent article sur le passage des paramètres en C #. Il détaille clairement le comportement exact et l'utilisation des paramètres de passage par valeur, par référence (ref) et par sortie (out).

Voici une citation importante de cette page en relation avec les paramètres ref:

Les paramètres de référence ne transmettent pas les valeurs des variables utilisées dans l'appel de membre de fonction - ils utilisent les variables elles-mêmes. Plutôt que de créer un nouvel emplacement de stockage pour la variable dans la déclaration de membre de fonction, le même emplacement de stockage est utilisé, de sorte que la valeur de la variable dans le membre de fonction et la valeur du paramètre de référence seront toujours les mêmes. Les paramètres de référence nécessitent le modificateur ref dans le cadre de la déclaration et de l'appel - cela signifie qu'il est toujours clair lorsque vous passez quelque chose par référence.

17
Noldorin

Très bien expliqué ici: http://msdn.Microsoft.com/en-us/library/s6938f28.aspx

Résumé de l'article:

Une variable d'un type de référence ne contient pas directement ses données; il contient une référence à ses données. Lorsque vous transmettez un paramètre de type référence par valeur, il est possible de modifier les données pointées par la référence, telles que la valeur d'un membre de classe. Cependant, vous ne pouvez pas modifier la valeur de la référence elle-même; c'est-à-dire que vous ne pouvez pas utiliser la même référence pour allouer de la mémoire à une nouvelle classe et la faire persister en dehors du bloc. Pour ce faire, passez le paramètre à l'aide du mot clé ref ou out.

15
himanshupareek66

Lorsque vous transmettez un type de référence avec le mot clé ref, vous transmettez la référence par référence et la méthode que vous appelez peut affecter une nouvelle valeur au paramètre. Cette modification se propagera à la portée appelante. Sans ref, la référence est passée par valeur, et cela ne se produit pas.

C # a également le mot clé 'out' qui ressemble beaucoup à ref, sauf qu'avec 'ref', les arguments doivent être initialisés avant d'appeler la méthode, et avec 'out' vous devez assigner une valeur dans la méthode de réception.

9
Rytmis

Il vous permet de modifier la référence transmise. Ex.

void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

void Baz(ref Foo y)
{
    y.Name = "2";

    // Overwrite the reference
    y = new Foo();
}

Vous pouvez également utiliser out si vous ne vous souciez pas de la référence transmise:

void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}
5
HVS

Un autre tas de code

class O
{
    public int prop = 0;
}

class Program
{
    static void Main(string[] args)
    {
        O o1 = new O();
        o1.prop = 1;

        O o2 = new O();
        o2.prop = 2;

        o1modifier(o1);
        o2modifier(ref o2);

        Console.WriteLine("1 : " + o1.prop.ToString());
        Console.WriteLine("2 : " + o2.prop.ToString());
        Console.ReadLine();
    }

    static void o1modifier(O o)
    {
        o = new O();
        o.prop = 3;
    }

    static void o2modifier(ref O o)
    {
        o = new O();
        o.prop = 4;
    }
}
4
Svend

En plus des réponses existantes:

Comme vous avez demandé la différence des 2 méthodes: Il n'y a pas de variance co (ntra) lors de l'utilisation de ref ou out:

class Foo { }
class FooBar : Foo { }

static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }

void Main()
{
    Foo foo = null;
    Bar(foo);           // OK
    Bar(ref foo);       // OK

    FooBar fooBar = null;
    Bar(fooBar);        // OK (covariance)
    Bar(ref fooBar);    // compile time error
}
3
springy76

Un paramètre dans une méthode semble toujours passer une copie, la question est une copie de quoi. Une copie est effectuée par un constructeur de copie pour un objet et puisque toutes les variables sont Object en C #, je pense que c'est le cas pour toutes. Les variables (objets) sont comme des personnes vivant à certaines adresses. Nous changeons les personnes vivant à ces adresses ou nous pouvons créer plus de références aux personnes vivant à ces adresses dans l'annuaire téléphonique (faire des copies superficielles). Ainsi, plusieurs identifiants peuvent faire référence à la même adresse. Les types de référence souhaitent plus d'espace, donc contrairement aux types de valeur qui sont directement connectés par une flèche à leur identifiant dans la pile, ils ont une valeur pour une autre adresse dans le tas (un espace plus grand pour demeurer). Cet espace doit être pris du tas.

Type de valeur: Indentifier (contient valeur = adresse de la valeur de pile) ----> Valeur du type de valeur

Type de référence: Identifiant (contient valeur = adresse de valeur de pile) ----> (contient valeur = adresse de valeur de tas) ----> Valeur de tas (contient le plus souvent des adresses vers d'autres valeurs), imaginez plus de flèches collant dans différents directions vers Array [0], Array [1], array [2]

La seule façon de changer une valeur est de suivre les flèches. Si une flèche est perdue/modifiée dans la façon dont la valeur est inaccessible.

1
Petar Drianov