397 votes

Quand utiliser Réf vs out

Quelqu'un m'a demandé l'autre jour, quand ils devraient utiliser le paramètre mot-clé out au lieu de ref. Alors que j'ai (je pense) comprendre la différence entre le ref et out mots-clés (qui a été demandé avant) et la meilleure explication semble être que l' ref == in et out, ce sont certains (hypothétique ou code) exemples de cas où je devrais toujours utiliser out et pas ref.

Depuis ref est plus générale, pourquoi ne jamais vous souhaitez utiliser out? Est-il juste de sucre syntaxique?

409voto

peterchen Points 21792

Vous devez utiliser out , sauf si vous avez besoin d' ref.

Cela fait une grande différence lorsque les données doivent être triées par exemple, à un autre processus, qui peut être coûteux. Donc, vous voulez éviter les gares de la valeur initiale lorsque la méthode n'a pas à en faire usage.

Au-delà, il montre aussi le lecteur de la déclaration ou de l'appel si la valeur initiale est pertinent (et peut-être conservé), ou jetés.

Comme une différence mineure, un paramètre de sortie ne doit pas être initialisé.

Exemple pour out:

string a, b;
person.GetBothNames(out a, out b);

où GetBothNames est une méthode pour récupérer les deux valeurs de façon atomique, la méthode ne change pas de comportement quel que soit a et b le sont. Si l'appel est acheminé vers un serveur à Hawaii, en copiant les valeurs initiales d'ici à Hawaii est un gaspillage de bande passante. Une semblable extrait à l'aide de réf.:

string a = String.Empty, b = String.Empty;
person.GetBothNames(ref a, ref b);

pourrait semer la confusion chez les lecteurs, parce qu'il semble que les valeurs initiales de a et de b sont (bien que le nom de la méthode pourrait indiquer qu'elles ne le sont pas).

Exemple pour ref:

string name = textbox.Text;
bool didModify = validator.SuggestValidName(ref name);

Ici, la valeur initiale est pertinente à la méthode.

76voto

Reed Copsey Points 315315

Utilisez pour indiquer que le paramètre n'est pas utilisé, uniquement la valeur. Cela aide à l’appelant de comprendre que vous êtes toujours initialiser le paramètre.

Aussi, ref et out ne sont pas seulement pour les types valeur. Ils vous permettent également de réinitialiser l’objet qui référence un type référence de dans une méthode.

41voto

Adam Robinson Points 88472

Vous avez raison en ce que, sémantiquement, ref fournit à la fois "dans" et "hors" de la fonctionnalité, alors que out seulement fournit de la "fonctionnalité". Il y a quelques choses à considérer:

  1. out exige que la méthode d'accepter le paramètre DOIT, à un certain moment avant de retourner, d'attribuer une valeur à la variable. Vous trouverez ce modèle dans certains de la valeur/clé de stockage de données des classes comme l' Dictionary<K,V>, où vous avez des fonctions comme TryGetValue. Cette fonction prend un out paramètre qui détient ce que la valeur sera récupéré. Il ne serait pas logique pour l'appelant pour passer une valeur dans cette fonction, de sorte qu' out est utilisé pour garantir que la valeur sera dans la variable après l'appel, même si elle n'est pas "réel" de données (dans le cas d' TryGetValue où la clé n'est pas présente).
  2. out et ref paramètres sont assemblés différemment lorsqu'ils traitent avec de l'interopérabilité code

Aussi, en aparté, il est important de noter que, bien que les types de référence et de la valeur des types diffèrent par la nature de leur valeur, chaque variable dans vos points d'application à un emplacement de mémoire qui contient une valeur, même pour les types référence. Il se trouve simplement que, avec les types de référence, la valeur contenue dans cet emplacement de la mémoire est un autre emplacement de mémoire. Lorsque vous transmettez des valeurs d'une fonction (ou de faire toute autre affectation de variable), la valeur de cette variable est copiée dans l'autre variable. Pour les types de valeur, ce qui signifie que l'ensemble du contenu du type est copié. Pour les types référence, cela signifie que l'emplacement de mémoire est copié. De toute façon, il permet de créer une copie des données contenues dans la variable. La seule réelle importance que cela est traite avec attribution de la sémantique; lors de l'affectation d'une variable ou d'un passage par valeur (par défaut), lors d'une nouvelle affectation se fait à l'original (ou nouveau) de la variable, il n'a pas d'incidence sur les autres variables. Dans le cas de types de référence, oui, les modifications apportées à l' instance sont disponibles sur les deux côtés, mais c'est parce que la variable est un pointeur vers un autre emplacement de mémoire; le contenu de la variable-l'emplacement de la mémoire--n'a pas fait de changement.

En passant par l' ref mot-clé dit que la variable d'origine et le paramètre de la fonction sera effectivement pointer vers le même emplacement mémoire. Encore une fois, cela n'affecte que la cession de la sémantique. Si une nouvelle valeur est affectée à une variable, ensuite parce que l'autre pointe vers le même emplacement de mémoire la nouvelle valeur sera répercuté sur l'autre côté.

28voto

Lo Sauer Points 5469

Il dépend de la compilation contexte (Voir Exemple ci-dessous).

out et ref à la fois désigner la variable passage par référence, pourtant, ref implique que la variable est initialisée avant d'être votée, ce qui peut être une différence importante dans le contexte de Regroupement (Interop: UmanagedToManagedTransition ou vice versa)

MSDN avertit:

Do not confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same. A method parameter can be modified by ref regardless of whether it is a value type or a reference type. There is no boxing of a value type when it is passed by reference.

De la officiel MSDN Docs:

The out keyword causes arguments to be passed by reference. This is similar to the ref keyword, except that ref requires that the variable be initialized before being passed

The ref keyword causes an argument to be passed by reference, not by value. The effect of passing by reference is that any change to the parameter in the method is reflected in the underlying argument variable in the calling method. The value of a reference parameter is always the same as the value of the underlying argument variable.

On peut vérifier que les ref sont en effet les mêmes lorsque l'argument est affectée:

CIL Exemple:

Considérons l'exemple suivant

static class outRefTest{
    public static int myfunc(int x){x=0; return x; }
    public static void myfuncOut(out int x){x=0;}
    public static void myfuncRef(ref int x){x=0;}
    public static void myfuncRefEmpty(ref int x){}
    // Define other methods and classes here
}

dans CIL, les instructions d' myfuncOut et myfuncRef sont identiques comme prévu.

outRefTest.myfunc:
IL_0000:  nop         
IL_0001:  ldc.i4.0    
IL_0002:  starg.s     00 
IL_0004:  ldarg.0     
IL_0005:  stloc.0     
IL_0006:  br.s        IL_0008
IL_0008:  ldloc.0     
IL_0009:  ret         

outRefTest.myfuncOut:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldc.i4.0    
IL_0003:  stind.i4    
IL_0004:  ret         

outRefTest.myfuncRef:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldc.i4.0    
IL_0003:  stind.i4    
IL_0004:  ret         

outRefTest.myfuncRefEmpty:
IL_0000:  nop         
IL_0001:  ret         

nop: aucune opération, ldloc: la charge locale, stloc: pile locale, ldarg: charge argument, bs.s: direction de la cible....

(Voir: Liste des CIL instructions )

18voto

zneak Points 45458

Vous devez utiliser ref si vous envisagez de lire et d'écrire dans le paramètre. Vous devez utiliser out si vous n'avez l'intention d'écrire. En effet, out est pour quand vous pourriez avoir besoin de plus d'une valeur de retour, ou lorsque vous ne souhaitez pas utiliser le normal mécanisme de retour pour la sortie (mais ce doit être rare).

Il y a le langage de la mécanique qui aident ces cas d'utilisation. Ref paramètres doivent avoir été initialisée avant qu'ils sont passés à une méthode (en mettant l'accent sur le fait qu'ils sont en lecture-écriture), et out paramètres ne peuvent pas être lues avant ils sont affectés à une valeur, et sont garantis d'avoir été écrit à la fin de la méthode (en mettant l'accent sur le fait qu'ils sont en écriture seulement). Contrevenir à ces principes de résultats à une erreur de compilation.

int x;
Foo(ref x); // error: x is uninitialized

void Bar(out int x) {}  // error: x was not written to

Par exemple, int.TryParse renvoie un bool et accepte un out int paramètre:

int value;
if (int.TryParse(numericString, out value))
{
    /* numericString was parsed into value, now do stuff */
}
else
{
    /* numericString couldn't be parsed */
}

C'est un exemple clair d'une situation où vous avez besoin pour la sortie des deux valeurs: le résultat numérique et si la conversion a réussi ou pas. Les auteurs de la CLR décidé d'opter pour out ici car ils ne se soucient pas de ce que l' int aurait pu être avant.

Pour ref, vous pouvez regarder Interlocked.Increment:

int x = 4;
Interlocked.Increment(ref x);

Interlocked.Increment atomiquement incrémente la valeur de x. Puisque vous avez besoin de lire x de l'incrémenter, c'est une situation où l' ref est plus approprié. Vous êtes tout à fait se soucient de ce x était avant il a été adopté à l' Increment.

Dans la prochaine version de C#, il sera même possible de déclarer des variables en out paramètres, en ajoutant encore plus l'accent sur leur sortie-seulement la nature:

if (int.TryParse(numericString, out int value))
{
    // 'value' exists and was declared in the `if` statement
}
else
{
    // conversion didn't work, 'value' doesn't exist here
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X