web-dev-qa-db-fra.com

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

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 devrais-je utiliser et pourquoi?

833
TK.

ref indique au compilateur que l'objet est initialisé avant d'entrer dans la fonction, tandis que out indique au compilateur que l'objet sera initialisé à l'intérieur de la fonction.

Ainsi, alors que ref est bidirectionnel, out est en lecture seule.

1101
Rune Grimstad

Le modificateur ref signifie que:

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

Le modificateur out signifie que:

  1. La valeur n'est pas définie et ne peut pas être lue par la méthode jusqu'à elle est définie.
  2. La méthode must la définit avant de retourner.
500
Anton Kolesov

Supposons que Dom se présente au box de Peter à propos du mémo concernant les rapports de TPS.

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

Si Dom était un argument de poids, il demanderait à Peter d'imprimer une nouvelle copie de la note de service à emporter avec lui.

139
Michael Blackburn

Je vais essayer une explication:

Je pense que nous comprenons comment les types de valeur fonctionnent bien? Les types de valeur sont (int, long, struct, etc.). Lorsque vous les envoyez à une fonction sans commande ref, il copie les données. Tout ce que vous faites avec ces données dans la fonction n'affecte que la copie, pas l'original. La commande ref envoie les données réelles et toute modification aura une incidence sur les données extérieures à 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 objet , deux parties sont créées:

  1. Le bloc de mémoire qui contient les données pour un objet .
  2. Une référence (pointeur) à ce bloc de données.

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

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

Deux références pointant sur le même objet. Si vous modifiez une propriété sur un objet à l'aide de référence2, cela affectera les mêmes données pointées par référence1.

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

Si vous annulez la référence2 ou la dirigez vers de nouvelles données, cela n'affectera pas la référence1 et les données référencées par la référence1.

(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? Le référence réelle à un objet est envoyé à la méthode. Donc, vous avez maintenant 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 équivaut à envoyer un objet non par référence, sauf pour deux choses principales:

1) Lorsque vous annulez la référence dans la méthode, elle annulera celle en dehors 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
53
James Roland

ref est in et out .

Vous devez utiliser out de préférence là où cela suffit pour vos besoins.

28
Ruben Bartelink

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 de sortie est renvoyé sous forme de 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.

ref:

En C #, lorsque vous passez un type de valeur tel que int, float, double etc. comme argument du paramètre de méthode, il est passé par valeur. Par conséquent, si vous modifiez la valeur du paramètre, cela n'affectera pas l'argument dans l'appel de la méthode. Mais si vous marquez le paramètre avec le mot clé "ref", il sera reflété 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"?
17
Nazmul Hasan

Étendre le chien, exemple de chat. La deuxième méthode avec ref change 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;
    }
12
BBB

Puisque vous passez dans un type de référence (une classe), il n’est pas nécessaire d’utiliser ref car, par défaut, seul un référence à l’objet réel est passé et vous changez donc toujours l’objet derrière. le 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 n'avez pas à utiliser ref si vous souhaitez modifier l'objet dans votre méthode.

8
Albic

ref et out se comportent de la même manière, à l'exception des différences suivantes.

  • La variable ref doit être initialisée avant utilisation. La variable out peut être utilisée sans affectation
  • Le paramètre out doit être traité comme une valeur non attribuée par la fonction qui l'utilise. Nous pouvons donc utiliser le paramètre initialisé out dans le code appelant, mais la valeur sera perdue lors de l'exécution de la fonction.
8
gmail user

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

J'ai créé quelques exemples minimaux de référence, de sortie et autres pour illustrer ce point. Je ne couvre pas les meilleures pratiques, mais juste des exemples pour comprendre les différences.

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

7
2upmedia

"Boulanger"

C'est parce que le premier change votre référence de chaîne pour qu'elle pointe vers "Baker". La modification de la référence est possible car vous l'avez transmise via le mot clé ref (=> une référence à une référence à une chaîne). Le second appel obtient une copie de la référence à la chaîne.

la chaîne a l'air spéciale au début. Mais string 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 contenant le texte "capable"! 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 indiquons 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 "En mesure" car vous venez de définir 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’ya tout simplement aucune méthode ou propriété qui modifie une instance de chaîne existante (vous pouvez essayer d’en trouver une dans la documentation, mais vous n’en réglerez aucune :-)). Toutes les méthodes de manipulation de chaîne renvoient une nouvelle instance de chaîne! (C'est pourquoi vous obtenez souvent de meilleures performances lorsque vous utilisez la classe StringBuilder)

6
mmmmmmmm

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, celle-ci doit être affectée au paramètre out. Il n'est pas permis de le laisser non assigné.

5
Farhan S.

Out: Une instruction return peut être utilisée pour renvoyer une seule valeur d'une fonction. Toutefois, à l'aide des paramètres de sortie, vous pouvez renvoyer deux valeurs d'une fonction. Les paramètres de sortie sont similaires aux 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 ceci:

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();

      }
   }
}

ref: Un paramètre de référence est une référence à un emplacement de mémoire d'une variable. Lorsque vous transmettez des paramètres par référence, contrairement aux paramètres de valeur, aucun nouvel emplacement de stockage n'est 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 montre ceci:

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();

      }
   }
}
4
Faisal Naseer

ref and out fonctionne exactement comme passer des références et 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);
4
RotatingWheel

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) Runtime alloue une mémoire sur la pile à # 00, suffisamment large pour stocker une adresse (# 00 = myList, car les noms de variable ne sont en réalité que des alias pour les emplacements de mémoire).

(4) Runtime crée un objet de liste sur le tas à l'emplacement mémoire #FF (toutes ces adresses sont par exemple des sakés)

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

Retour à Authoring Time:

(6) Nous passons ensuite l'objet List comme argument myParamList à la méthode appelée modifyMyList et lui affectons 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) Runtime lance la routine d'appel pour la méthode appelée et, dans le cadre de celle-ci, vérifie le type des paramètres.

(8) Lorsqu'il trouve le type de référence, il alloue une mémoire sur la pile en 04 pour aliaser la variable de paramètre myParamList.

(9) Il enregistre également la valeur #FF.

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

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


Le mot clé ref est une directive du compilateur permettant d'éviter la génération du code d'exécution pour (8) et (9), ce qui signifie qu'il il n'y aura pas d'allocation de tas pour les paramètres de méthode. Il utilisera le pointeur # 00 d'origine pour agir sur l'objet en #FF. Si le pointeur d'origine n'est pas initialisé, le moteur d'exécution cessera de se plaindre de l'impossibilité de poursuivre 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 chose que ref avec une légère modification en (9) et ( dix). Le compilateur s'attend à ce que l'argument ne soit pas initialisé et continuera avec (8), (4) et (5) de créer un objet sur le tas et de stocker son adresse de départ dans la variable d'argument. Aucune erreur non initialisée ne sera générée et toute référence précédente stockée sera perdue.

4
supi

Ci-dessous, j'ai montré un exemple en utilisant à la fois Ref et out. Maintenant, vous serez tous débarrassés de l’arbitrage.

Dans l'exemple ci-dessous, mentionné lorsque je commente // myRefObj = new myClass {Name = "réf à l'extérieur appelé !!"}; ligne, une erreur se produira en indiquant "Utilisation d'une variable locale non affectée ' myRefObj '", mais il n'y a pas d'erreur de ce type dans out.

Où utiliser Ref: lorsque nous appelons une procédure avec un paramètre in et 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 que 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; }
} 
1
Ankur Bhutani

Ref: Le mot clé ref est utilisé pour passer un argument en tant que 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 d'appel avant de le transmettre à la méthode appelée.

Out: le mot clé out est également utilisé pour passer un argument tel que le mot clé ref, mais l'argument peut être passé sans lui attribuer de valeur. Un argument passé à l'aide d'un mot clé out doit être initialisé dans la méthode appelée avant de retourner à 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 in method surcharge

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

1
Dejan Ciev
 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 utiliserez "ref", cela signifie que vous initialisez déjà int/string

mais lorsque vous utilisez "out", cela fonctionne dans les deux conditions, que vous initialisiez ou non int/string, mais que vous devez initialiser int/string dans cette fonction.

1
Haris Zia

Ils sont à peu près les mêmes - la seule différence est qu’une variable que vous transmettez en tant que paramètre out n’a pas besoin d’être initialisée et que la méthode utilisant le paramètre ref doit lui attribuer une valeur quelconque.

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

Les paramètres de référence concernent les données susceptibles d'être modifiées. Les paramètres de sortie correspondent aux données constituant une sortie supplémentaire pour la fonction (par exemple, int.TryParse) qui utilisent déjà la valeur de retour.

1
Talha Khan

Si vous voulez passer votre paramètre en tant que ref, vous devez l’initialiser avant de passer le paramètre à la fonction. Le compilateur lui-même affichera l’erreur. méthode.Vous pouvez initialiser l'objet dans la méthode d'appel elle-même.

0
Rakeshkumar Das

En plus de vous permettre de réaffecter la variable de quelqu'un d'autre à une instance différente d'une classe, renvoyer plusieurs valeurs, etc., tilisez ref ou out permet à quelqu'un d'autre de savoir ce dont vous avez besoin et ce vous avez l'intention de faire avec la variable qu'ils fournissent

  • Vous n'avez pas besoinref ou out si tout ce que vous allez faire est de modifier les choses à l'intérieur l'instance MyClass qui est passée dans l'argument someClass.

    • La méthode appelante verra des changements tels que someClass.Message = "Hello World", que vous utilisiez ref, out ou rien
    • L'écriture de someClass = new MyClass() à l'intérieur de myFunction(someClass) remplace l'objet vu par la variable someClass uniquement dans le cadre de la méthode myFunction. La méthode appelante est toujours au courant de l'instance originale MyClass qu'elle a créée et transmise à votre méthode.
  • Vous besoinref ou out si vous prévoyez d'échanger la someClass contre un nouvel objet et que vous souhaitez que la méthode d'appel voie votre modification.

    • Ecrire someClass = new MyClass() dans myFunction(out someClass) modifie l'objet vu par la méthode appelée myFunction

Il existe d'autres programmeurs

Et ils veulent savoir ce que vous allez faire avec 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 avec leurs variables quand ils appellent vos méthodes

  • Utiliser ref donne comme instruction "Transmettez une variable affectée à une valeur lorsque vous appelez ma méthode. Sachez que je pourrais la remplacer par quelque chose de nouveau au cours de ma méthode. Ne vous attendez pas à ce que votre variable soit pointant vers le vieil objet quand j'ai fini "

  • Utiliser out donne la déclaration suivante: "Passe une variable de substitution à ma méthode. Qu'elle ait une valeur ou non, le compilateur va m'obliger à l'affecter à une nouvelle valeur. Je garantis absolument que le objet pointé par votre variable avant d'appeler ma méthode, sera différent lorsque j'aurai terminé

A propos, en C # 7.2, il y a aussi un modificateur in

Et cela empêche la méthode de permuter l'instance passée en instance pour une instance différente. Pensez-y comme si vous disiez à ces millions de développeurs "transmettez-moi votre référence de variable originale et je vous promets de ne pas échanger vos données soigneusement élaborées contre quelque chose d'autre". in a quelques particularités, et dans certains cas, comme dans le cas où une conversion implicite pourrait être nécessaire pour rendre votre court métrage compatible avec un in int, le compilateur fera temporairement un int, élargira votre court métrage à celui-ci, transmettez-le. par référence et finir. Cela peut le faire parce que vous avez déclaré que vous ne le feriez pas.


Microsoft a fait cela avec les méthodes .TryParse sur les types numériques:

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

En marquant le paramètre comme suit: out ils déclarent activement ici "nous allons changer définitivement votre valeur soigneusement élaborée de 98234957 pour quelque chose autre"

Bien sûr, ils doivent le faire, par exemple pour analyser des types de valeur, car si la méthode parse n’était pas autorisée à échanger le type de valeur contre autre chose, cela ne fonctionnerait pas très bien. Mais imaginez qu’il existe une méthode fictive dans certains cas. 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 à calculer des chiffres, vous 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 réussir ce cours parfait. Il va certainement être jeté et remplacé par PoorlyNamedMethod

0
Caius Jard

Du point de vue d’une méthode qui reçoit un paramètre, la différence entre ref et out est que C # exige que les méthodes écrivent dans chaque paramètre out avant de renvoyer, et ne doivent rien faire avec Un tel paramètre, autre que le passer sous la forme d'un paramètre out ou l'écrire, jusqu'à ce qu'il soit passé sous la forme d'un paramètre out à une autre méthode ou écrit directement. Notez que certaines autres langues n'imposent pas de telles exigences; une méthode virtuelle ou une méthode d'interface déclarée en C # avec un paramètre out 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, dans de nombreuses circonstances, C # supposera que lors de l'appel d'une méthode avec un paramètre out, la variable transmise sera écrite sans avoir été lue auparavant. 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 myDictionary identifie une implémentation IDictionary<TKey,TValue> écrite dans un langage autre que le langage C #, même si MyStruct s = new MyStruct(myDictionary); ressemble à une affectation, il est possible que s ne soit pas modifié.

Notez que les constructeurs écrits en VB.NET, contrairement à ceux en C #, ne supposent pas que les méthodes appelées modifieront les paramètres out et effaceront tous les champs de manière inconditionnelle. Le comportement étrange mentionné ci-dessus ne se produira pas avec du code entièrement écrit 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.

0
supercat