Quelle est la différence entre les mots clés «ref» et «out»?

892

Je crée une fonction où je dois passer un objet pour qu'il puisse être modifié par la fonction. Quelle est la différence entre:

public void myFunction(ref MyClass someClass)

et

public void myFunction(out MyClass someClass)

Que dois-je utiliser et pourquoi?

TK.
la source
69
Vous: J'ai besoin de passer un objet pour qu'il puisse être modifié Il ressemble à MyClassun classtype, c'est-à-dire un type de référence. Dans ce cas, l'objet que vous passez peut être modifié par le myFunctionmême sans mot ref- outclé / . myFunctionrecevra une nouvelle référence qui pointe vers le même objet, et il peut modifier ce même objet autant qu'il le souhaite. La différence que refferait le mot - clé serait qu'il recevrait myFunctionla même référence au même objet. Cela ne serait important que si l' myFunctionon changeait la référence pour pointer vers un autre objet.
Jeppe Stig Nielsen
3
Je suis perplexe devant la quantité de réponses déroutantes ici, quand @ AnthonyKolesov est assez parfait.
o0 '.
La déclaration d'une méthode out est utile lorsque vous souhaitez qu'une méthode renvoie plusieurs valeurs. Un argument peut être affecté à null. Cela permet aux méthodes de renvoyer des valeurs en option.
Yevgraf Andreyevich Zhivago
Ici expliqué avec Exemple Il est plus compréhensible :) dotnet-tricks.com/Tutorial/csharp/…
Prageeth godage
2
@ Le commentaire de JeppeStigNielsen est, techniquement, la (seule) bonne réponse à la vraie question du PO. Pour passer un objet dans une méthode afin que la méthode puisse modifier l'objet , passez simplement l'objet (référence à) dans la méthode par valeur. La modification de l'objet dans la méthode via l'argument objet modifie l'objet d'origine , même si la méthode contient sa propre variable distincte (qui fait référence au même objet).
David R Tribble

Réponses:

1162

refindique au compilateur que l'objet est initialisé avant d'entrer dans la fonction, tandis qu'il outindique au compilateur que l'objet sera initialisé à l'intérieur de la fonction.

Donc, alors que refc'est bidirectionnel, outc'est uniquement en sortie.

Rune Grimstad
la source
270
Une autre chose intéressante spécifique à out est que la fonction doit être affectée au paramètre out. Il n'est pas permis de le laisser sans affectation.
Daniel Earwicker
7
'ref' s'applique-t-il uniquement au type de valeur? Puisque le type de référence est toujours passé par ref.
défectueux
3
Oui. Types de valeurs, y compris les structures
Rune Grimstad
17
@faulty: Non, ref n'est pas uniquement applicable aux types de valeurs. ref / out sont comme des pointeurs en C / C ++, ils traitent de l'emplacement mémoire de l'objet (indirectement en C #) au lieu de l'objet direct.
Thr
52
@faulty: Contre-intuitivement, les types de référence sont toujours passés par valeur en C #, sauf si vous utilisez le spécificateur ref. Si vous définissez myval = somenewval, l'effet est uniquement dans la portée de cette fonction. Le mot clé ref vous permettrait de changer myval pour pointer vers somenewval.
JasonTrue
535

Le refmodificateur signifie que:

  1. La valeur est déjà définie et
  2. La méthode peut le lire et le modifier.

Le outmodificateur signifie que:

  1. La valeur n'est pas définie et ne peut pas être lue par la méthode tant qu'elle n'est pas définie.
  2. La méthode doit la définir avant de revenir.
Anton Kolesov
la source
30
Cette réponse explique le plus clairement et de manière concise les restrictions que le compilateur impose lors de l'utilisation du mot clé out par opposition au mot clé ref.
Dr.Wily's Apprentice
5
À partir de MSDN: Un paramètre ref doit être initialisé avant utilisation, tandis qu'un paramètre out n'a pas à être explicitement initialisé avant d'être transmis et toute valeur précédente est ignorée.
Shiva Kumar
1
Avec out, peut-il être lu du tout dans la méthode, avant d'être défini par cette méthode, s'il a été initialisé avant l'appel de la méthode? Je veux dire, la méthode appelée peut-elle lire ce que la méthode appelante lui a passé comme argument?
Panzercrisis
3
Panzercrisis, pour "out", la méthode appelée peut lire si elle est déjà définie. mais il faut le remettre.
robert jebakumar2
146

Disons que Dom se présente dans la cabine de Peter à propos du mémo sur les rapports TPS.

Si Dom était un argument de référence, il aurait une copie imprimée du mémo.

Si Dom était un argument, il ferait imprimer à Peter une nouvelle copie du mémo pour qu'il l'emporte avec lui.

Michael Blackburn
la source
54
ref Dom aurait écrit le rapport au crayon pour que Peter puisse le modifier
Deebster
6
@Deebster vous savez, cette métaphore ne vous a jamais rien fait, pourquoi devez-vous la torturer ainsi? ;)
Michael Blackburn
21
divertissant mais instructif, stackoverflow a besoin de plus de messages comme celui-ci
Frank Visaggio
2
Au cas où quelqu'un trouverait cette réponse à moitié amusante, regardez le film "Office Space".
displayName
et le patron de Dom et Peters resterait derrière Dom (comme argument), forçant les deux à travailler à l'imprimer à nouveau jusqu'à ce que Peter remette l'impression à Domd
Patrick Artner
57

Je vais essayer une explication:

Je pense que nous comprenons comment fonctionnent les types de valeur? Les types de valeurs sont (int, long, struct etc.). Lorsque vous les envoyez dans une fonction sans commande ref, il COPIE les données . Tout ce que vous faites pour ces données dans la fonction affecte uniquement la copie, pas l'original. La commande ref envoie les données ACTUELLES et toute modification affectera les données en dehors de la fonction.

Ok sur la partie déroutante, types de référence:

Permet de créer un type de référence:

List<string> someobject = new List<string>()

Lorsque vous créez un nouvel objet , deux parties sont créées:

  1. Le bloc de mémoire qui contient les données de certains objets .
  2. Une référence (pointeur) vers ce bloc de données.

Maintenant, lorsque vous envoyez un objet dans une méthode sans référence, il COPIE le pointeur de référence , PAS les données. Vous avez donc maintenant ceci:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

Deux références pointant vers le même objet. Si vous modifiez une propriété sur un objet en utilisant reference2, cela affectera les mêmes données pointées par reference1.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

Si vous annulez reference2 ou le pointez vers de nouvelles données, cela n'affectera pas reference1 ni les données reference1 pointées.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

Maintenant, que se passe-t-il lorsque vous envoyez un objet par référence à une méthode? La référence réelle à someobject est envoyée à la méthode. Vous n'avez donc plus qu'une seule référence aux données:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

mais qu'est ce que ça veut dire? Cela revient exactement à envoyer un objet non par référence, sauf pour deux choses principales:

1) Lorsque vous annulez la référence à l'intérieur de la méthode, celle-ci sera annulée à l'extérieur de la méthode.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2) Vous pouvez maintenant pointer la référence vers un emplacement de données complètement différent et la référence en dehors de la fonction pointera maintenant vers le nouvel emplacement de données.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true
James Roland
la source
Vous voulez dire après tout (dans le cas de référence) qu'il n'y a qu'une seule référence aux données mais deux alias pour elles. Droite?
Sadiq
3
A voté pour l'explication claire. Mais je pense que cela ne répond pas à la question, car cela n'explique pas la différence entre les paramètres refet out.
Joyce Babu
1
Incroyable. pouvez-vous expliquer la même chose que pour le outmot-clé?
Asif Mushtaq
28

ref est dans et hors .

Vous devez utiliser outde préférence partout où cela suffit pour vos besoins.

Ruben Bartelink
la source
pas tout à fait, comme la réponse acceptée ref si directionnelle et inutile en ignorant les types de valeur si elle n'est pas renvoyée.
kenny
@kenny: Pouvez-vous clarifier un peu s'il vous plaît - c.-à-d., quels mots changeriez-vous pour maintenir l'esprit de la réponse tout en supprimant l'inexactitude que vous percevez? Ma réponse n'est pas une supposition folle d'un débutant, mais la hâte (lacune, fautes de frappe) dans votre commentaire semble le supposer. L'objectif est de proposer une manière de penser la différence avec le moins de mots.
Ruben Bartelink
(BTW Je connais les types de valeur, les types de référence, passant par référence, passant par valeur, COM et C ++ si vous trouvez utile de faire référence à ces concepts dans votre clarification)
Ruben Bartelink
1
Les références aux objets sont passées par valeur (sauf lors de l'utilisation du mot clé "ref" ou "out"). Considérez les objets comme des numéros d'identification. Si une variable de classe contient "Object # 1943" et que l'on transmet cette variable par valeur à une routine, cette routine peut apporter des modifications à Object # 1943, mais elle ne peut pas faire pointer la variable vers autre chose que "Object # 1943". Si la variable était passée par référence, la routine pourrait faire en sorte que le point variable contienne "Objet # 5441".
supercat
1
@supercat: J'aime bien votre explication de ref vs val (et cette anaologie de suivi). Je pense que kenny n'a pas vraiment besoin que tout cela lui soit expliqué, (relativement) déroutant comme l'ont été ses commentaires. Je souhaite que nous puissions tous supprimer ces putains de commentaires, car ils déroutent tout le monde. La cause première de toutes ces absurdités semble être que kenny a mal lu ma réponse et n'a pas encore signalé un seul mot qui devrait être ajouté / supprimé / remplacé. Aucun de nous trois n'a appris quoi que ce soit de la discussion que nous ne connaissions pas déjà et l'autre réponse a un nombre ridicule de votes positifs.
Ruben Bartelink
18

en dehors:

En C #, une méthode ne peut renvoyer qu'une seule valeur. Si vous souhaitez renvoyer plusieurs valeurs, vous pouvez utiliser le mot clé out. Le modificateur out retourne comme retour par référence. La réponse la plus simple est que le mot clé «out» est utilisé pour obtenir la valeur de la méthode.

  1. Vous n'avez pas besoin d'initialiser la valeur dans la fonction appelante.
  2. Vous devez affecter la valeur dans la fonction appelée, sinon le compilateur signalera une erreur.

réf:

En C #, lorsque vous passez un type de valeur tel que int, float, double etc. comme argument au paramètre de méthode, il est passé par valeur. Par conséquent, si vous modifiez la valeur du paramètre, cela n'affecte pas l'argument dans l'appel de méthode. Mais si vous marquez le paramètre avec le mot clé "ref", il se reflétera dans la variable réelle.

  1. Vous devez initialiser la variable avant d'appeler la fonction.
  2. Il n'est pas obligatoire d'attribuer une valeur au paramètre ref dans la méthode. Si vous ne changez pas la valeur, quelle est la nécessité de la marquer comme «ref»?
Nazmul Hasan
la source
"En C #, une méthode ne peut renvoyer qu'une seule valeur. Si vous souhaitez renvoyer plusieurs valeurs, vous pouvez utiliser le mot clé out." Nous pouvons également utiliser "ref" pour renvoyer de la valeur. Nous pouvons donc utiliser à la fois ref et out si nous voulons retourner plusieurs valeurs à partir d'une méthode?
Ned
1
Dans le c # 7, vous pouvez renvoyer plusieurs valeurs avec ValueTuples.
Iman Bahrampour
13

Extension du chien, exemple de chat. La deuxième méthode avec ref modifie l'objet référencé par l'appelant. D'où "Chat" !!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }
BBB
la source
8

Puisque vous passez un type de référence (une classe), il n'est pas nécessaire d'utiliser ref car par défaut, seule une référence à l'objet réel est transmise et, par conséquent, vous modifiez toujours l'objet derrière la référence.

Exemple:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

Tant que vous passez dans une classe, vous ne devez pas l'utiliser refsi vous souhaitez modifier l'objet à l'intérieur de votre méthode.

Albic
la source
5
Cela ne fonctionne que si aucun nouvel objet n'est créé et renvoyé. Lorsqu'un nouvel objet est créé, la référence à l'ancien objet est perdue.
etsuba
8
C'est faux - essayez ce qui suit: ajouter someObject = nullpour Barterminer l'exécution. Votre code fonctionnera correctement car seule Barla référence à l'instance a été annulée. Aujourd'hui , le changement Barà Bar(ref MyClass someObject)et exécuter à nouveau - vous aurez une NullReferenceExceptionraison Foode » la référence à l'instance a été Nulled aussi.
Keith
8

refet outse comportent de la même manière, à l'exception des différences suivantes.

  • refLa variable doit être initialisée avant utilisation. outla variable peut être utilisée sans affectation
  • outLe paramètre doit être traité comme une valeur non affectée par la fonction qui l'utilise. Ainsi, nous pouvons utiliser un outparamètre initialisé dans le code appelant, mais la valeur sera perdue lors de l'exécution de la fonction.
utilisateur gmail
la source
8

Pour ceux qui apprennent par l'exemple (comme moi), voici ce que dit Anthony Kolesov .

J'ai créé quelques exemples minimaux de ref, out et d'autres pour illustrer le point. Je ne couvre pas les meilleures pratiques, juste des exemples pour comprendre les différences.

https://gist.github.com/2upmedia/6d98a57b68d849ee7091

2upmedia
la source
6

"Boulanger"

C'est parce que le premier change votre référence de chaîne pour pointer sur "Baker". La modification de la référence est possible car vous l'avez passée via le mot clé ref (=> une référence à une référence à une chaîne). Le deuxième appel obtient une copie de la référence à la chaîne.

la chaîne ressemble à une sorte de spécial au premier abord. Mais la chaîne n'est qu'une classe de référence et si vous définissez

string s = "Able";

alors s est une référence à une classe de chaîne qui contient le texte "Able"! Une autre affectation à la même variable via

s = "Baker";

ne modifie pas la chaîne d'origine, mais crée simplement une nouvelle instance et laissons s pointer vers cette instance!

Vous pouvez l'essayer avec le petit exemple de code suivant:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

Qu'attendez-vous? Ce que vous obtiendrez est toujours "Able" car vous définissez simplement la référence dans s sur une autre instance tandis que s2 pointe sur l'instance d'origine.

EDIT: la chaîne est également immuable, ce qui signifie qu'il n'y a tout simplement pas de méthode ou de propriété qui modifie une instance de chaîne existante (vous pouvez essayer d'en trouver une dans la documentation mais vous n'en aurez aucune :-)). Toutes les méthodes de manipulation de chaînes retournent une nouvelle instance de chaîne! (C'est pourquoi vous obtenez souvent de meilleures performances lorsque vous utilisez la classe StringBuilder)

mmmmmmmm
la source
1
Exactement. Il n'est donc pas strictement vrai de dire "Puisque vous passez dans un type de référence (une classe), il n'est pas nécessaire d'utiliser ref".
Paul Mitchell
En théorie, il est juste de le dire parce qu'il a écrit "afin qu'il puisse être modifié", ce qui n'est pas possible sur les chaînes. Mais à cause des objets immuables "ref" et "out" sont très utiles aussi pour les types de référence! (.Net contient beaucoup de classes immuables!)
mmmmmmmm
Oui tu as raison. Je n'ai pas pensé à des objets immuables comme des chaînes car la plupart des objets sont mutables.
Albic
1
Eh bien, c'est une réponse déroutante à voir dans LQP, bien sûr; il n'y a rien de mal à cela, sauf qu'il semble être une réponse longue et approfondie à un autre commentaire (puisque la question d'origine ne mentionne Able et Baker dans aucune de ses révisions), comme s'il s'agissait d'un forum. Je suppose que cela n'a pas encore été vraiment réglé.
Nathan Tuggy
6

ref signifie que la valeur du paramètre ref est déjà définie, la méthode peut la lire et la modifier. Utiliser le mot clé ref revient à dire que l'appelant est responsable de l'initialisation de la valeur du paramètre.


out indique au compilateur que l'initialisation de l'objet est la responsabilité de la fonction, la fonction doit assigner au paramètre out. Il n'est pas permis de le laisser sans affectation.

Farhan S.
la source
5

Out: une instruction return peut être utilisée pour renvoyer une seule valeur d'une fonction. Cependant, à l'aide des paramètres de sortie, vous pouvez renvoyer deux valeurs à partir d'une fonction. Les paramètres de sortie sont comme des paramètres de référence, sauf qu'ils transfèrent des données hors de la méthode plutôt que dans celle-ci.

L'exemple suivant illustre cela:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

réf: Un paramètre de référence est une référence à un emplacement mémoire d'une variable. Lorsque vous passez des paramètres par référence, contrairement aux paramètres de valeur, un nouvel emplacement de stockage n'est pas créé pour ces paramètres. Les paramètres de référence représentent le même emplacement de mémoire que les paramètres réels fournis à la méthode.

En C #, vous déclarez les paramètres de référence à l'aide du mot clé ref. L'exemple suivant le démontre:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}
Faisal Naseer
la source
4

ref et out fonctionnent comme en passant par des références et en passant par des pointeurs comme en C ++.

Pour ref, l'argument doit être déclaré et initialisé.

Pour out, l'argument doit être déclaré mais peut ou non être initialisé

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);
RotatingWheel
la source
1
Vous pouvez déclarer une ligne variable: out double Half_nbr.
Sebastian Hofmann
4

Temps de création:

(1) Nous créons la méthode d'appel Main()

(2) il crée un objet List (qui est un objet de type référence) et le stocke dans la variable myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

Pendant l'exécution:

(3) Le runtime alloue une mémoire sur la pile à # 00, suffisamment large pour stocker une adresse (# 00 = myList, car les noms de variables ne sont en réalité que des alias pour les emplacements de mémoire)

(4) Le runtime crée un objet liste sur le tas à l'emplacement mémoire #FF (toutes ces adresses sont par exemple des sakes)

(5) Le runtime stockerait alors l'adresse de départ #FF de l'objet à # 00 (ou en mots, stocke la référence de l'objet List dans le pointeur myList)

Retour à l'heure de création:

(6) Nous passons ensuite l'objet List en argument myParamListà la méthode appelée modifyMyListet lui assignons un nouvel objet List

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Pendant l'exécution:

(7) Le Runtime démarre la routine d'appel pour la méthode appelée et, dans le cadre de celle-ci, vérifie le type de paramètres.

(8) Après avoir trouvé le type de référence, il alloue une mémoire sur la pile à # 04 pour aliaser la variable de paramètre myParamList.

(9) Il y stocke également la valeur #FF.

(10) Le runtime crée un objet liste sur le tas à l'emplacement de mémoire # 004 et remplace #FF dans # 04 par cette valeur (ou a déréférencé l'objet List d'origine et pointé vers le nouvel objet List dans cette méthode)

L'adresse dans # 00 n'est pas modifiée et conserve la référence à #FF (ou le myListpointeur d' origine n'est pas perturbé).


Le mot clé ref est une directive du compilateur pour ignorer la génération de code d'exécution pour (8) et (9), ce qui signifie qu'il n'y aura pas d'allocation de tas pour les paramètres de méthode. Il utilisera le pointeur # 00 d'origine pour opérer sur l'objet à #FF. Si le pointeur d'origine n'est pas initialisé, le runtime s'arrête de se plaindre qu'il ne peut pas continuer car la variable n'est pas initialisée

Le mot-clé out est une directive de compilation qui est à peu près la même que ref avec une légère modification en (9) et (10). Le compilateur s'attend à ce que l'argument ne soit pas initialisé et continuera avec (8), (4) et (5) pour créer un objet sur le tas et pour stocker son adresse de départ dans la variable d'argument. Aucune erreur non initialisée ne sera levée et toute référence précédente stockée sera perdue.

supi
la source
3

En plus de vous permettre de réaffecter la variable de quelqu'un d'autre à une autre instance d'une classe, de renvoyer plusieurs valeurs, etc., en utilisant refou en outlaissant quelqu'un d'autre savoir ce dont vous avez besoin et ce que vous avez l'intention de faire avec la variable qu'il fournit

  • Vous n'avez pas besoin ref ou outsi tout ce que vous allez faire est de modifier les choses à l' intérieur de l' MyClassinstance qui est passée dans l'argument someClass.

    • La méthode d'appel verra des changements comme someClass.Message = "Hello World"si vous utilisez ref, outou rien
    • L'écriture à l' someClass = new MyClass()intérieur myFunction(someClass)remplace l'objet vu par le someClassdans le champ d'application de la myFunctionméthode uniquement. La méthode appelante connaît toujours l' MyClassinstance d' origine qu'elle a créée et transmise à votre méthode
  • Vous avez besoin ref ou outsi vous envisagez de remplacer le someClasspar un nouvel objet entier et que la méthode appelante voit votre changement

    • L'écriture à l' someClass = new MyClass()intérieur myFunction(out someClass)change l'objet vu par la méthode qui a appelémyFunction

D'autres programmeurs existent

Et ils veulent savoir ce que vous allez faire de leurs données. Imaginez que vous écrivez une bibliothèque qui sera utilisée par des millions de développeurs. Vous voulez qu'ils sachent ce que vous allez faire de leurs variables lorsqu'ils appellent vos méthodes

  • L'utilisation reffait une déclaration de «Passer une variable affectée à une certaine valeur lorsque vous appelez ma méthode. Soyez conscient que je pourrais la changer complètement pour autre chose au cours de ma méthode. Ne vous attendez pas à ce que votre variable pointe vers l'ancien objet quand j'ai fini "

  • L'utilisation outfait une déclaration de "Passer une variable d'espace réservé à ma méthode. Peu importe qu'elle ait une valeur ou non; le compilateur me forcera à l'attribuer à une nouvelle valeur. Je garantis absolument que l'objet pointé par votre variable avant d'appeler ma méthode, sera différente lorsque j'aurai terminé

Soit dit en passant, en C # 7.2, il y a aussi un inmodificateur

Et cela empêche la méthode d'échanger l'instance passée pour une autre instance. Pensez-y comme dire à ces millions de développeurs "passez-moi votre référence de variable d'origine, et je promets de ne pas échanger vos données soigneusement conçues pour autre chose". ina quelques particularités, et dans certains cas, comme lorsqu'une conversion implicite peut être nécessaire pour rendre votre court-métrage compatible avec un, in intle compilateur fera temporairement un int, élargira votre court-métrage, le passera par référence et terminera. Il peut le faire parce que vous avez déclaré que vous n'allez pas jouer avec.


Microsoft l'a fait avec les .TryParseméthodes sur les types numériques:

int i = 98234957;
bool success = int.TryParse("123", out i);

En signalant le paramètre comme outils déclarent activement ici "nous allons définitivement changer votre valeur minutieusement conçue de 98234957 pour autre chose"

Bien sûr, ils doivent le faire, pour des choses comme l'analyse des types de valeurs, car si la méthode d'analyse n'était pas autorisée à échanger le type de valeur pour autre chose, cela ne fonctionnerait pas très bien .. Mais imaginez qu'il y ait une méthode fictive dans certains bibliothèque que vous créez:

public void PoorlyNamedMethod(out SomeClass x)

Vous pouvez voir que c'est un out, et vous pouvez donc savoir que si vous passez des heures à chiffrer les chiffres, créez la SomeClass parfaite:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Eh bien, c'était une perte de temps, prendre toutes ces heures pour faire ce cours parfait. Il va certainement être jeté et remplacé par PoorlyNamedMethod

Caius Jard
la source
3

Pour ceux qui cherchent une réponse concise.

Les mots-clés refet outsont utilisés pour passer à côté reference.


Une variable de refmot - clé doit avoir une valeur ou doit faire référence à un objet ou null avant son passage.


Contrairement à ref, une variable de outmot - clé doit avoir une valeur ou doit faire référence à un objet ou null après son passage ainsi que pas besoin d'avoir une valeur ou faire référence à un objet avant de passer.

snr
la source
2

Pour illustrer les nombreuses excellentes explications, j'ai développé l'application console suivante:

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorld: Une copie de StringListnamed LiStriest transmise. Au début de la méthode, cette copie fait référence à la liste d'origine et peut donc être utilisée pour modifier cette liste. LiStriRéférence ultérieurement un autre List<string>objet à l'intérieur de la méthode qui n'affecte pas la liste d'origine.

  • HalloWelt: LiStriRefest un alias du déjà initialisé ListStringRef. L' List<string>objet passé est utilisé pour initialiser un nouveau, refétait donc nécessaire.

  • CiaoMondo: LiStriOutest un alias de ListStringOutet doit être initialisé.

Ainsi, si une méthode modifie simplement l'objet référencé par la variable transmise, le compilateur ne vous laissera pas utiliser outet vous ne devriez pas l'utiliser refcar cela confondrait non pas le compilateur mais le lecteur du code. Si la méthode fait que l'argument passé référence un autre objet, utilisez-le refpour un objet déjà initialisé et outpour les méthodes qui doivent initialiser un nouvel objet pour l'argument passé. En plus de cela, refet outagissez de même.

Dietrich Baumgarten
la source
1

Ils sont à peu près les mêmes - la seule différence est qu'une variable que vous passez en tant que paramètre de sortie n'a pas besoin d'être initialisée, et la méthode utilisant le paramètre ref doit la définir sur quelque chose.

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

Les paramètres de référence sont pour les données qui pourraient être modifiées, les paramètres de sortie sont pour les données qui sont une sortie supplémentaire pour la fonction (par exemple int.TryParse) qui utilisent déjà la valeur de retour pour quelque chose.

Talha Khan
la source
1

Ci-dessous, j'ai montré un exemple utilisant à la fois Ref et out . Maintenant, vous serez tous débarrassés des références et des sorties.

Dans l'exemple mentionné ci-dessous lorsque je commente // myRefObj = new myClass {Name = "ref outside called !!"}; , obtiendra une erreur indiquant "Utilisation de la variable locale non affectée 'myRefObj'" , mais il n'y a pas une telle erreur in out .

Où utiliser Ref : lorsque nous appelons une procédure avec un paramètre in et que le même paramètre sera utilisé pour stocker la sortie de ce proc.

Où utiliser Out: lorsque nous appelons une procédure sans paramètre in et le même paramètre sera utilisé pour renvoyer la valeur de ce proc. Notez également la sortie

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 
Ankur Bhutani
la source
1
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

vous pouvez vérifier ce code, il vous décrira sa différence complète lorsque vous utilisez "ref", cela signifie que vous initialisez déjà cet int / chaîne

mais lorsque vous utilisez "out" cela fonctionne dans les deux conditions si u initialise cette chaîne int / ou non mais u doit initialiser cette chaîne int / dans cette fonction

Haris Zia
la source
1

Ref: Le mot clé ref est utilisé pour passer un argument comme référence. Cela signifie que lorsque la valeur de ce paramètre est modifiée dans la méthode, elle est reflétée dans la méthode appelante. Un argument transmis à l'aide d'un mot clé ref doit être initialisé dans la méthode appelante avant d'être transmis à la méthode appelée.

Out: Le mot-clé out est également utilisé pour passer un argument comme le mot-clé ref, mais l'argument peut être passé sans lui attribuer de valeur. Un argument transmis à l'aide d'un mot clé out doit être initialisé dans la méthode appelée avant de revenir à la méthode appelante.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

Ref et out en surcharge de méthode

Ref et out ne peuvent pas être utilisés simultanément en surcharge de méthode. Cependant, ref et out sont traités différemment au moment de l'exécution, mais ils sont traités de la même manière au moment de la compilation (CLR ne fait pas de différence entre les deux lorsqu'il a créé IL pour ref et out).

Dejan Ciev
la source
0

Du point de vue d'une méthode qui reçoit un paramètre, la différence entre refet outest que C # exige que les méthodes écrivent dans chaque outparamètre avant de retourner, et ne doivent rien faire avec un tel paramètre, autre que le passer en tant que outparamètre ou y écrire , jusqu'à ce qu'il soit transmis en tant que outparamètre à une autre méthode ou écrit directement. Notez que certaines autres langues n'imposent pas de telles exigences; une méthode virtuelle ou d'interface déclarée en C # avec un outparamètre peut être remplacée dans un autre langage qui n'impose aucune restriction particulière à ces paramètres.

Du point de vue de l'appelant, C # supposera dans de nombreuses circonstances qu'en appelant une méthode avec un outparamètre, la variable transmise sera écrite sans avoir été lue en premier. Cette hypothèse peut ne pas être correcte lors de l'appel de méthodes écrites dans d'autres langues. Par exemple:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

Si myDictionaryidentifie une IDictionary<TKey,TValue>implémentation écrite dans un langage autre que C #, même si elle MyStruct s = new MyStruct(myDictionary);ressemble à une affectation, elle peut potentiellement rester sinchangée.

Notez que les constructeurs écrits en VB.NET, contrairement à ceux en C #, ne font aucune hypothèse quant à savoir si les méthodes appelées modifieront les outparamètres et effaceront tous les champs sans condition. Le comportement étrange mentionné ci-dessus ne se produira pas avec du code écrit entièrement en VB ou entièrement en C #, mais peut se produire lorsque le code écrit en C # appelle une méthode écrite en VB.NET.

supercat
la source
0

Si vous souhaitez passer votre paramètre en tant que référence, vous devez l'initialiser avant de passer le paramètre à la fonction, sinon le compilateur lui-même affichera l'erreur.Mais en cas de paramètre out, vous n'avez pas besoin d'initialiser le paramètre objet avant de le passer à la Vous pouvez initialiser l'objet dans la méthode appelante elle-même.

Rakeshkumar Das
la source
-3

Gardez bien à l'esprit que le paramètre de référence qui est passé à l'intérieur de la fonction est directement travaillé.

Par exemple,

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Cela écrira Dog, not Cat. Par conséquent, vous devez travailler directement sur someObject.

Mangesh Pimpalkar
la source
6
Bien que tout ici soit à peu près vrai, cela n'explique pas vraiment la différence entre la valeur par référence ou la sortie. Au mieux, il explique à moitié la différence entre les types de référence et de valeur / immuables.
Conrad Frix
Si vous voulez que ce code écrive cat, passez cet objet avec la touche 'ref' comme ceci: public static void Bar (ref MyClass someObject), Bar (ref myObject);
Daniel Botero Correa
-4

Je ne suis peut-être pas si bon dans ce domaine, mais les chaînes (même si elles sont techniquement des types de référence et vivent sur le tas) sont transmises par valeur, pas par référence?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

C'est pourquoi vous avez besoin de ref si vous souhaitez que les modifications existent en dehors de la portée de la fonction qui les effectue, vous ne passez pas de référence autrement.

Pour autant que je sache, vous n'avez besoin que de ref pour les types structs / value et de la chaîne elle-même, car string est un type de référence qui prétend qu'il l'est mais n'est pas un type de valeur.

Je peux me tromper complètement ici, je suis nouveau.

Edwin
la source
5
Bienvenue dans Stack Overflow, Edwin. Les chaînes sont passées par référence, comme tout autre objet, pour autant que je sache. Vous pouvez être confus car les chaînes sont des objets immuables, il n'est donc pas aussi évident qu'elles sont passées par référence. Imaginez que la chaîne ait une méthode appelée Capitalize()qui changerait le contenu de la chaîne en lettres majuscules. Si vous avez ensuite remplacé votre ligne a = "testing";par a.Capitalize();, votre sortie serait "BONJOUR", pas "Bonjour". L'un des avantages des types immuables est que vous pouvez faire circuler les références et ne pas vous soucier de la modification de la valeur par un autre code.
Don Kirkby
2
Il existe trois types fondamentaux de sémantique qu'un type peut exposer: la sémantique de référence mutable, la sémantique de valeur mutable et la sémantique immuable. Considérons les variables x et y d'un type T, qui a le champ ou la propriété m, et supposons que x est copié dans y. Si T a une sémantique de référence, les changements de xm seront observés par ym Si T a une sémantique de valeur, on peut changer xm sans affecter ym Si T a une sémantique immuable, ni xm ni ym ne changeront jamais. La sémantique immuable peut être simulée par des objets de référence ou de valeur. Les chaînes sont des objets de référence immuables.
supercat