Qual è la differenza tra le parole chiave "ref" e "out"?


892

Sto creando una funzione in cui devo passare un oggetto in modo che possa essere modificato dalla funzione. Qual è la differenza tra:

public void myFunction(ref MyClass someClass)

e

public void myFunction(out MyClass someClass)

Quale dovrei usare e perché?


69
Tu: ho bisogno di passare un oggetto in modo che possa essere modificato. Sembra MyClassun classtipo, cioè un tipo di riferimento. In tal caso, l'oggetto che passi può essere modificato myFunctionanche da no ref/ outparola chiave. myFunctionriceverà un nuovo riferimento che punta allo stesso oggetto e può modificare lo stesso oggetto quanto vuole. La differenza che reffarebbe la parola chiave sarebbe che ha myFunctionricevuto lo stesso riferimento allo stesso oggetto. Ciò sarebbe importante solo se myFunctioncambiassi il riferimento per puntare a un altro oggetto.
Jeppe Stig Nielsen,

3
Sono perplesso dalla quantità di risposte confuse qui, quando @ AnthonyKolesov è abbastanza perfetto.
o0 '.

La dichiarazione di un metodo out è utile quando si desidera che un metodo restituisca più valori. Un argomento può essere assegnato a null. Ciò consente ai metodi di restituire valori facoltativamente.
Yevgraf Andreyevich Zhivago,

Qui spiegato con Esempio più comprensibile :) dotnet-tricks.com/Tutorial/csharp/…
Prageeth godage

2
@ Il commento di JeppeStigNielsen è, tecnicamente, la (sola) risposta corretta alla vera domanda dell'OP. Per passare un oggetto in un metodo in modo che il metodo possa modificare l'oggetto , è sufficiente passare l'oggetto (riferimento all'oggetto) nel metodo in base al valore. La modifica dell'oggetto nel metodo tramite l'argomento object modifica l'oggetto originale , anche se il metodo contiene una propria variabile separata (che fa riferimento allo stesso oggetto).
David R Tribble,

Risposte:


1162

refdice al compilatore che l'oggetto è inizializzato prima di entrare nella funzione, mentre outdice al compilatore che l'oggetto sarà inizializzato all'interno della funzione.

Quindi, mentre refè bidirezionale, outè solo.


270
Un'altra cosa interessante specifica di out è che la funzione deve assegnare al parametro out. Non è consentito lasciarlo non assegnato.
Daniel Earwicker,

7
'ref' è applicabile solo al tipo di valore? Poiché il tipo di riferimento è sempre passato dal rif.
difettoso

3
Sì. Tipi di valore comprese le strutture
Rune Grimstad

17
@faulty: No, ref non è applicabile solo ai tipi di valore. ref / out sono come puntatori in C / C ++, si occupano della posizione di memoria dell'oggetto (indirettamente in C #) anziché dell'oggetto diretto.
thr

52
@faulty: controintuitivamente, i tipi di riferimento vengono sempre passati per valore in C #, a meno che non si usi l'identificatore di riferimento. Se si imposta myval = somenewval, l'effetto è solo nell'ambito di tale funzione. La parola chiave ref ti permetterebbe di cambiare myval in modo da puntare a somenewval.
JasonTrue,

535

Il refmodificatore significa che:

  1. Il valore è già impostato e
  2. Il metodo può leggerlo e modificarlo.

Il outmodificatore significa che:

  1. Il valore non è impostato e non può essere letto dal metodo fino a quando non viene impostato.
  2. Il metodo deve impostarlo prima di tornare.

30
Questa risposta spiega in modo più chiaro e conciso le restrizioni che il compilatore impone quando utilizza la parola chiave out anziché la parola chiave ref.
Apprendista Dr. Wily,

5
Da MSDN: un parametro ref deve essere inizializzato prima dell'uso, mentre un parametro out non deve essere inizializzato esplicitamente prima di essere passato e qualsiasi valore precedente viene ignorato.
Shiva Kumar,

1
Con out, può essere letto affatto all'interno del metodo, prima che sia stato impostato da quel metodo, se è stato inizializzato prima che il metodo fosse chiamato? Voglio dire, il metodo chiamato può leggere ciò che il metodo chiamante gli ha passato come argomento?
Panzercrisis,

3
Panzercrisis, per "out", il metodo chiamato può leggere se è già impostato. ma deve reimpostarlo.
robert jebakumar2,

146

Diciamo che Dom si presenta al cubicolo di Peter sul memo sui rapporti TPS.

Se Dom fosse un argomento di riferimento, avrebbe una copia stampata del promemoria.

Se Dom fosse un argomento fuori discussione, farebbe stampare a Peter una nuova copia del memo da portare con sé.


54
ref Dom avrebbe scritto il rapporto a matita in modo che Peter potesse modificarlo
Deebster,

6
@Deebster, sai, quella metafora non ti ha mai fatto niente, perché devi torturarla così? ;)
Michael Blackburn,

21
divertente ma istruttivo, StackOverflow ha bisogno di più post come questo
Frank Visaggio,

2
Nel caso in cui qualcuno trovi questa risposta solo per metà divertente, per favore guarda il film "Office Space".
displayName

e il capo di Dom e Peters rimarrebbero dietro Dom (come argomento fuori discussione), costringendo entrambi a lavorare per stamparlo di nuovo fino a quando Peter consegna a Domd la stampa
Patrick Artner,

57

Ho intenzione di cimentarmi in una spiegazione:

Penso che capiamo come funzionano i tipi di valore? I tipi di valore sono (int, long, struct ecc.). Quando li invii a una funzione senza un comando ref, COPIA i dati . Qualunque cosa tu faccia a quei dati nella funzione ha effetto solo sulla copia, non sull'originale. Il comando ref invia i dati EFFETTIVI e qualsiasi modifica influirà sui dati esterni alla funzione.

Ok per la parte confusa, tipi di riferimento:

Consente di creare un tipo di riferimento:

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

Quando si rinnova un oggetto , vengono create due parti:

  1. Il blocco di memoria che contiene i dati per qualche oggetto .
  2. Un riferimento (puntatore) a quel blocco di dati.

Ora, quando si invia un oggetto in un metodo senza ref, COPIA il puntatore di riferimento , NON i dati. Quindi ora hai questo:

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

Due riferimenti che puntano allo stesso oggetto. Se modifichi una proprietà su qualche oggetto usando reference2, questo influenzerà gli stessi dati a cui fa riferimento reference1.

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

Se annulli riferimento2 o lo punti a nuovi dati, ciò non influirà su riferimento1 né su riferimento1.

(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

Cosa succede quando si invia un oggetto tramite ref a un metodo? Il riferimento effettivo a someobject viene inviato al metodo. Quindi ora hai un solo riferimento ai dati:

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

Ma cosa significa? Agisce esattamente come inviare un oggetto non per ref, tranne per due cose principali:

1) Quando annulli il riferimento all'interno del metodo, questo annullerà quello esterno al metodo.

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

2) Ora è possibile puntare il riferimento a una posizione dei dati completamente diversa e il riferimento esterno alla funzione ora punta alla nuova posizione dei dati.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

Intendi dopo tutto (nel caso ref) c'è solo un riferimento ai dati ma due alias per esso. Giusto?
Sadiq,

3
Eseguito l'upgrade per la chiara spiegazione. Ma penso che questo non risponda alla domanda, in quanto non spiega la differenza tra refe outparametri.
Joyce Babu,

1
Sorprendente. puoi spiegare lo stesso della outparola chiave?
Asif Mushtaq,

28

ref è dentro e fuori .

Dovresti usare le outpreferenze ovunque siano sufficienti per le tue esigenze.


non del tutto, poiché la risposta accettata si riferisce se direzionale e inutile ignorando i tipi di valore se non passati.
Kenny,

@kenny: Puoi chiarire un po ', per favore, ad es. quali parole cambieresti per mantenere lo spirito della risposta ma rimuovendo l'inaccuratezza che percepisci? La mia risposta non è un'ipotesi folle da parte di un principiante, ma la fretta (terseness, errori di battitura) nel tuo commento sembra supporre che lo sia. L'obiettivo è fornire un modo di pensare alla differenza con il minor numero di parole.
Ruben Bartelink,

(A proposito, ho familiarità con i tipi di valore, i tipi di riferimento, il passaggio per riferimento, il passaggio per valore, COM e C ++ se ritieni utile fare riferimento a questi concetti nel tuo chiarimento)
Ruben Bartelink

1
I riferimenti agli oggetti vengono passati per valore (tranne quando si utilizza la parola chiave "ref" o "out"). Pensa agli oggetti come numeri ID. Se una variabile di classe contiene "Oggetto # 1943" e si passa quella variabile in base al valore a una routine, tale routine può apportare modifiche all'oggetto # 1943, ma non può fare in modo che la variabile punti a qualcosa di diverso da "Oggetto # 1943". Se la variabile è stata passata per riferimento, la routine potrebbe mantenere il punto variabile "Oggetto # 5441".
supercat

1
@supercat: mi piace la tua spiegazione di ref vs val (e questo anaology di follow-up). Penso che Kenny in realtà non abbia bisogno di nulla di tutto ciò spiegato a lui, (relativamente) confuso come erano i suoi commenti. Vorrei che tutti potessimo semplicemente rimuovere questi dannati commenti, dato che confondono tutti. La causa principale di tutte queste assurdità sembra essere che Kenny ha letto male la mia risposta e deve ancora sottolineare una sola parola che dovrebbe essere aggiunta / rimossa / sostituita. Nessuno di noi tre ha imparato nulla dalla discussione che non conoscevamo già e l'altra risposta ha un numero ridicolo di voti.
Ruben Bartelink,

18

su:

In C #, un metodo può restituire solo un valore. Se desideri restituire più di un valore, puoi utilizzare la parola chiave out. Il modificatore out restituisce come ritorno per riferimento. La risposta più semplice è che la parola chiave "out" viene utilizzata per ottenere il valore dal metodo.

  1. Non è necessario inizializzare il valore nella funzione chiamante.
  2. È necessario assegnare il valore nella funzione chiamata, altrimenti il ​​compilatore segnalerà un errore.

ref:

In C #, quando si passa un tipo di valore come int, float, double ecc. Come argomento al parametro del metodo, viene passato per valore. Pertanto, se si modifica il valore del parametro, non influisce sull'argomento nella chiamata al metodo. Ma se si contrassegna il parametro con la parola chiave "ref", si rifletterà nella variabile effettiva.

  1. È necessario inizializzare la variabile prima di chiamare la funzione.
  2. Non è obbligatorio assegnare alcun valore al parametro ref nel metodo. Se non si modifica il valore, qual è la necessità di contrassegnarlo come "ref"?

"In C #, un metodo può restituire un solo valore. Se desideri restituire più di un valore, puoi utilizzare la parola chiave out." Possiamo anche usare "ref" per restituire valore. Quindi possiamo usare sia ref che out se vogliamo restituire più valori da un metodo?
Ned,

1
In c # 7 puoi restituire più valori con ValueTuples.
Iman Bahrampour,

13

Estensione del cane, esempio Cat. Il secondo metodo con ref modifica l'oggetto a cui fa riferimento il chiamante. Quindi "Gatto" !!!

    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;
    }

8

Dal momento che si passa in un tipo di riferimento (una classe), non è necessario utilizzarlo refperché per impostazione predefinita viene passato solo un riferimento all'oggetto reale e pertanto si cambia sempre l'oggetto dietro il riferimento.

Esempio:

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";
}

Finché passi in una classe non devi usare refse vuoi cambiare l'oggetto all'interno del tuo metodo.


5
Funziona solo se non viene creato e restituito alcun nuovo oggetto. Quando viene creato un nuovo oggetto, il riferimento al vecchio oggetto andrebbe perso.
etsuba,

8
Questo è sbagliato - prova quanto segue: aggiungi someObject = nullper Barterminare eseguire. Il codice verrà eseguito correttamente poiché solo Baril riferimento all'istanza è stato annullato. Ora cambia Bara Bar(ref MyClass someObject)ed eseguire di nuovo - si otterrà un NullReferenceExceptionperché Foo's riferimento all'istanza è stato annullato anche.
Keith,

8

refe si outcomportano in modo simile tranne che per le seguenti differenze.

  • refla variabile deve essere inizializzata prima dell'uso. outla variabile può essere utilizzata senza assegnazione
  • outIl parametro deve essere trattato come un valore non assegnato dalla funzione che lo utilizza. Quindi, possiamo usare il outparametro inizializzato nel codice chiamante, ma il valore andrà perso quando viene eseguita la funzione.


6

"Panettiere"

Questo perché il primo modifica il riferimento alla stringa in modo che punti a "Baker". La modifica del riferimento è possibile perché è stata passata tramite la parola chiave ref (=> un riferimento a un riferimento a una stringa). La seconda chiamata ottiene una copia del riferimento alla stringa.

all'inizio la stringa sembra una specie di speciale. Ma la stringa è solo una classe di riferimento e, se definita

string s = "Able";

allora s è un riferimento a una classe di stringhe che contiene il testo "In grado"! Un'altra assegnazione alla stessa variabile tramite

s = "Baker";

non cambia la stringa originale ma crea semplicemente una nuova istanza e puntiamo a quell'istanza!

Puoi provarlo con il seguente piccolo esempio di codice:

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

Cosa ti aspetti? Quello che otterrai sarà comunque "In grado" perché hai appena impostato il riferimento in s su un'altra istanza mentre s2 punta all'istanza originale.

EDIT: la stringa è anche immutabile, il che significa che semplicemente non esiste un metodo o una proprietà che modifica un'istanza di stringa esistente (puoi provare a trovarne una nei documenti ma non le pinne :-)). Tutti i metodi di manipolazione delle stringhe restituiscono una nuova istanza di stringa! (Ecco perché spesso ottieni prestazioni migliori quando usi la classe StringBuilder)


1
Esattamente. Quindi non è strettamente vero dire "Dato che stai passando in un tipo di riferimento (una classe) non è necessario usare ref".
Paul Mitchell,

In teoria è giusto dirlo perché ha scritto "in modo che possa essere modificato", cosa impossibile sulle stringhe. Ma a causa di oggetti immutabili "ref" e "out" sono molto utili anche per i tipi di riferimento! (.Net contiene molte classi immutabili!)
mmmmmmmm il

Sì hai ragione. Non pensavo agli oggetti immutabili come alle stringhe perché la maggior parte degli oggetti è mutevole.
Albic,

1
Bene, questa è una risposta sconcertante da vedere in LQP, per essere sicuri; non c'è nulla di sbagliato in questo, tranne che sembra essere una risposta lunga e approfondita a un altro commento (poiché la domanda originale menziona Able e Baker in nessuna delle sue revisioni), come se si trattasse di un forum. Immagino che non sia stato ancora risolto molto tempo fa quando.
Nathan Tuggy,

6

ref significa che il valore nel parametro ref è già impostato, il metodo può leggerlo e modificarlo. L'uso della parola chiave ref equivale a dire che il chiamante è responsabile dell'inizializzazione del valore del parametro.


out dice al compilatore che l'inizializzazione dell'oggetto è responsabilità della funzione, che la funzione deve assegnare al parametro out. Non è consentito lasciarlo non assegnato.


5

Out: un'istruzione return può essere utilizzata per restituire un solo valore da una funzione. Tuttavia, utilizzando i parametri di output, è possibile restituire due valori da una funzione. I parametri di output sono come parametri di riferimento, tranne per il fatto che trasferiscono i dati fuori dal metodo anziché all'interno.

L'esempio seguente illustra questo:

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 parametro di riferimento è un riferimento a una posizione di memoria di una variabile. Quando si passano i parametri per riferimento, diversamente dai parametri dei valori, non viene creata una nuova posizione di archiviazione per questi parametri. I parametri di riferimento rappresentano la stessa posizione di memoria dei parametri effettivi forniti al metodo.

In C #, dichiari i parametri di riferimento usando la parola chiave ref. L'esempio seguente lo dimostra:

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

ref e out funzionano esattamente come passare per riferimenti e passare per puntatori come in C ++.

Per ref, l'argomento deve essere dichiarato e inizializzato.

Per out, l'argomento deve essere dichiarato ma può o non può essere inizializzato

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

1
È possibile dichiarare una variabile di linea: out double Half_nbr.
Sebastian Hofmann,

4

Tempo di creazione:

(1) Creiamo il metodo di chiamata Main()

(2) crea un oggetto List (che è un oggetto del tipo di riferimento) e lo memorizza nella variabile myList.

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

Durante il runtime:

(3) Il runtime alloca una memoria nello stack a # 00, abbastanza ampia da memorizzare un indirizzo (# 00 = myList, poiché i nomi delle variabili sono in realtà solo alias per posizioni di memoria)

(4) Il runtime crea un oggetto elenco sull'heap nella posizione di memoria #FF (tutti questi indirizzi sono ad esempio validi)

(5) Il runtime memorizza quindi l'indirizzo iniziale #FF dell'oggetto su # 00 (o in parole, memorizza il riferimento dell'oggetto List nel puntatore myList)

Torna al tempo di creazione:

(6) Quindi passiamo l'oggetto List come argomento myParamListal metodo chiamato modifyMyListe gli assegniamo un nuovo oggetto 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;
}

Durante il runtime:

(7) Il runtime avvia la routine di chiamata per il metodo chiamato e, come parte di esso, controlla il tipo di parametri.

(8) Una volta trovato il tipo di riferimento, alloca una memoria nello stack al numero 04 per aliasare la variabile parametro myParamList.

(9) Quindi memorizza anche il valore #FF in esso.

(10) Il runtime crea un oggetto list sull'heap nella posizione di memoria # 004 e sostituisce #FF in # 04 con questo valore (o ha fatto riferimento all'oggetto List originale e ha indicato il nuovo oggetto List in questo metodo)

L'indirizzo in # 00 non viene modificato e mantiene il riferimento a #FF (o il myListpuntatore originale non viene disturbato).


La parola chiave ref è una direttiva del compilatore per saltare la generazione del codice di runtime per (8) e (9), il che significa che non ci sarà allocazione di heap per i parametri del metodo. Utilizzerà il puntatore # 00 originale per operare sull'oggetto in #FF. Se il puntatore originale non è inizializzato, il tempo di esecuzione si interromperà lamentandosi che non può procedere poiché la variabile non è inizializzata

La parola chiave out è una direttiva del compilatore che è praticamente la stessa di ref con una leggera modifica in (9) e (10). Il compilatore si aspetta che l'argomento non sia inizializzato e continuerà con (8), (4) e (5) per creare un oggetto su heap e memorizzare il suo indirizzo iniziale nella variabile argomento. Non verrà generato alcun errore non inizializzato e qualsiasi riferimento precedente memorizzato andrà perso.


3

Oltre a consentirti di riassegnare la variabile di qualcun altro a una diversa istanza di una classe, restituire più valori ecc., Utilizzare refo far outsapere a qualcun altro ciò di cui hai bisogno e cosa intendi fare con la variabile che forniscono

  • Lei non è necessario ref o outse tutto quello che stai andando a fare è cose Modifica dentro l' MyClassistanza che viene passato l'argomento someClass.

    • Il metodo chiamando vedrà cambiamenti come someClass.Message = "Hello World"se si utilizza ref, outo niente
    • La scrittura someClass = new MyClass()all'interno myFunction(someClass)scambia l'oggetto visto dal solo someClassnell'ambito del myFunctionmetodo. Il metodo chiamante conosce ancora l' MyClassistanza originale che ha creato e passato al tuo metodo
  • È necessario ref o outse si prevede di scambiare l'output someClasscon un oggetto completamente nuovo e si desidera che il metodo chiamante veda la modifica

    • Scrivere someClass = new MyClass()all'interno myFunction(out someClass)cambia l'oggetto visto dal metodo che ha chiamatomyFunction

Esistono altri programmatori

E vogliono sapere cosa farai con i loro dati. Immagina di scrivere una libreria che verrà utilizzata da milioni di sviluppatori. Vuoi che sappiano cosa farai con le loro variabili quando chiameranno i tuoi metodi

  • Utilizzo refrende una dichiarazione di "Passa una variabile assegnata a un valore quando chiami il mio metodo. Tieni presente che potrei cambiarlo per qualcos'altro interamente nel corso del mio metodo. Non aspettarti che la tua variabile punti al vecchio oggetto quando ho finito"

  • Utilizzo outrende una dichiarazione di "Passa una variabile segnaposto al mio metodo. Non importa se ha un valore oppure no; il compilatore mi costringerà ad assegnarlo a un nuovo valore. Garantisco assolutamente che l'oggetto indicato dal tuo variabile prima che tu abbia chiamato il mio metodo, sarà diverso dal momento in cui ho finito

A proposito, in C # 7.2 c'è anche un inmodificatore

Ciò impedisce al metodo di scambiare l'istanza passata con un'altra istanza. Pensalo come dire a quei milioni di sviluppatori "passami il tuo riferimento alla variabile originale e prometto di non scambiare i tuoi dati accuratamente elaborati con qualcos'altro". inha alcune peculiarità, e in alcuni casi come dove potrebbe essere necessaria una conversione implicita per rendere il tuo short compatibile con un in intcompilatore, farà temporaneamente un int, allargherà il tuo short ad esso, lo passerà per riferimento e finirà. Può farlo perché hai dichiarato che non hai intenzione di rovinarlo.


Microsoft ha fatto questo con i .TryParsemetodi sui tipi numerici:

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

Contrassegnando il parametro mentre outdichiarano attivamente qui " cambieremo sicuramente il vostro valore scrupoloso di 98234957 per qualcos'altro"

Certo, devono farlo, per cose come analizzare i tipi di valore perché se al metodo di analisi non fosse permesso scambiare il tipo di valore con qualcos'altro non funzionerebbe molto bene .. Ma immaginava che ci fosse un metodo fittizio in alcuni libreria che stai creando:

public void PoorlyNamedMethod(out SomeClass x)

Puoi vedere che è un oute puoi quindi sapere che se passi ore a scricchiolare i numeri, creando la SomeClass perfetta:

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

Beh, è ​​stata una perdita di tempo, impiegando tutte quelle ore per fare quella lezione perfetta. Sarà sicuramente gettato via e sostituito da PoorlyNamedMethod


3

Per chi cerca una risposta concisa.

Sia le parole chiave refche le outparole chiave vengono utilizzate per passare da reference.


Una variabile di refparola chiave deve avere un valore o deve fare riferimento a un oggetto o null prima del suo passaggio.


Diversamente ref, una variabile di outparola chiave deve avere un valore o deve riferirsi a un oggetto o null dopo il suo passaggio, così come non è necessario avere un valore o fare riferimento a un oggetto prima di passare.


2

Per illustrare le molte eccellenti spiegazioni, ho sviluppato la seguente app console:

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: Viene passata una copia del StringListnome LiStri. All'inizio del metodo, questa copia fa riferimento all'elenco originale e quindi può essere utilizzata per modificare questo elenco. Successivamente fa LiStririferimento a un altro List<string>oggetto all'interno del metodo che non influisce sull'elenco originale.

  • HalloWelt: LiStriRefè un alias del già inizializzato ListStringRef. L' List<string>oggetto passato viene utilizzato per inizializzare uno nuovo, quindi refera necessario.

  • CiaoMondo: LiStriOutè un alias di ListStringOute deve essere inizializzato.

Quindi, se un metodo modifica semplicemente l'oggetto a cui fa riferimento la variabile passata, il compilatore non ti permetterà di usarlo oute non dovresti usarlo refperché confonderebbe non il compilatore ma il lettore del codice. Se il metodo farà in modo che l'argomento passato faccia riferimento a un altro oggetto, utilizzare refper un oggetto già inizializzato e outper i metodi che devono inizializzare un nuovo oggetto per l'argomento passato. Oltre a ciò, refe outcomportati allo stesso modo.


1

Sono praticamente uguali: l'unica differenza è che una variabile che passi come parametro out non ha bisogno di essere inizializzata e il metodo che usa il parametro ref deve impostarlo su qualcosa.

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

I parametri di riferimento sono per i dati che potrebbero essere modificati, i parametri di uscita sono per i dati che sono un output aggiuntivo per la funzione (ad esempio int. TryParse) che stanno già utilizzando il valore restituito per qualcosa.


1

Di seguito ho mostrato un esempio usando sia Ref che out . Ora, sarete tutti chiariti su ref e out.

Nell'esempio citato di seguito quando commento // myRefObj = new myClass {Name = "ref outside chiamato !!"}; , visualizzerà un errore che dice "Uso della variabile locale non assegnata 'myRefObj'" , ma non si verifica alcun errore di questo tipo in uscita .

Dove usare Ref : quando chiamiamo una procedura con un parametro in e lo stesso parametro verrà usato per memorizzare l'output di quel proc.

Dove usare Out: quando chiamiamo una procedura con nessun parametro in e lo stesso parametro verrà usato per restituire il valore da quel proc. Notare anche l'output

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
 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;
     }

puoi controllare questo codice ti descriverà la sua completa differenza quando usi "ref" significa che hai già inizializzato quella int / stringa

ma quando usi "out" funziona in entrambe le condizioni quando inizializzi quell'int / string o no ma devi inizializzare quell'int / string in quella funzione


1

Rif: la parola chiave ref viene utilizzata per passare un argomento come riferimento. Ciò significa che quando il valore di quel parametro viene modificato nel metodo, viene riflesso nel metodo chiamante. Un argomento che viene passato utilizzando una parola chiave ref deve essere inizializzato nel metodo chiamante prima di essere passato al metodo chiamato.

Out: la parola chiave out viene utilizzata anche per passare un argomento come la parola chiave ref, ma l'argomento può essere passato senza assegnargli alcun valore. Un argomento che viene passato utilizzando una parola chiave out deve essere inizializzato nel metodo chiamato prima di tornare al metodo di chiamata.

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 e out nel metodo di sovraccarico

Sia ref che out non possono essere utilizzati contemporaneamente nel sovraccarico del metodo. Tuttavia, ref e out vengono trattati in modo diverso in fase di esecuzione ma vengono trattati allo stesso modo in fase di compilazione (CLR non distingue tra i due mentre ha creato IL per ref e out).


0

Dal punto di vista di un metodo che riceve un parametro, la differenza tra refe outè che C # richiede che i metodi debbano scrivere su ogni outparametro prima di tornare, e non debbano fare nulla con tale parametro, se non quello di passarlo come outparametro o scriverlo , fino a quando non è stato passato come outparametro a un altro metodo o scritto direttamente. Si noti che alcune altre lingue non impongono tali requisiti; un metodo virtuale o di interfaccia dichiarato in C # con un outparametro può essere ignorato in un'altra lingua che non impone restrizioni speciali a tali parametri.

Dal punto di vista del chiamante, in molte circostanze C # supporrà che quando si chiama un metodo con un outparametro, la variabile passata verrà scritta senza essere prima letta. Questa ipotesi potrebbe non essere corretta quando si chiamano metodi scritti in altre lingue. Per esempio:

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

Se myDictionaryidentifica IDictionary<TKey,TValue>un'implementazione scritta in una lingua diversa da C #, anche se MyStruct s = new MyStruct(myDictionary);sembra un compito, potrebbe potenzialmente snon essere modificata.

Si noti che i costruttori scritti in VB.NET, a differenza di quelli in C #, non fanno ipotesi sul fatto che i metodi chiamati modificheranno i outparametri e cancelleranno tutti i campi incondizionatamente. Il comportamento dispari di cui sopra non si verificherà con il codice scritto interamente in VB o interamente in C #, ma può verificarsi quando il codice scritto in C # chiama un metodo scritto in VB.NET.


0

Se si desidera passare il parametro come riferimento, è necessario inizializzarlo prima di passare il parametro alla funzione altrimenti il ​​compilatore stesso mostrerà l'errore, ma in caso di parametro out non è necessario inizializzare il parametro oggetto prima di passarlo al parametro method.You può inizializzare l'oggetto nel metodo di chiamata stesso.


-3

Ricorda bene che il parametro di riferimento che viene passato all'interno della funzione viene lavorato direttamente.

Per esempio,

    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;
    }

Questo scriverà Dog, non Cat. Quindi dovresti lavorare direttamente su someObject.


6
Mentre tutto qui è praticamente vero, non spiega davvero la differenza tra valore per riferimento o fuori. Nella migliore delle ipotesi, la metà spiega la differenza tra tipi di riferimento e valore / immutabili.
Conrad Frix,

Se vuoi che quel codice scriva cat, per favore passa quell'oggetto insieme al tasto 'ref' come questo: barra del vuoto statica pubblica (ref MyClass someObject), Bar (ref myObject);
Daniel Botero Correa,

-4

Potrei non essere così bravo in questo, ma sicuramente le stringhe (anche se tecnicamente sono tipi di riferimento e vivono nell'heap) vengono passate per valore, non per riferimento?

        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"!!!!

Questo è il motivo per cui è necessario ref se si desidera che le modifiche esistano al di fuori dell'ambito della funzione rendendole, altrimenti non si passa un riferimento.

Per quanto ne so, hai solo bisogno di ref per le strutture / i tipi di valore e la stringa stessa, poiché la stringa è un tipo di riferimento che finge di esserlo ma non è un tipo di valore.

Potrei sbagliarmi completamente qui, sono nuovo.


5
Benvenuto in Stack Overflow, Edwin. Le stringhe vengono passate per riferimento, proprio come qualsiasi altro oggetto, per quanto ne so. Potresti essere confuso perché le stringhe sono oggetti immutabili, quindi non è così ovvio che vengono passate per riferimento. Immagina che quella stringa abbia un metodo chiamato Capitalize()che cambierebbe il contenuto della stringa in lettere maiuscole. Se poi sostituissi la tua linea a = "testing";con a.Capitalize();, allora il tuo output sarebbe "CIAO", non "Ciao". Uno dei vantaggi dei tipi immutabili è che puoi passare i riferimenti e non preoccuparti di altri codici che cambiano il valore.
Don Kirkby,

2
Esistono tre tipi fondamentali di semantica che un tipo può esporre: semantica di riferimento mutabile, semantica di valore mutabile e semantica immutabile. Considera le variabili xey di un tipo T, che ha il campo o la proprietà m, e supponi che x sia copiato in y. Se T ha una semantica di riferimento, le modifiche a xm saranno osservate da ym Se T ha una semantica di valore, si può cambiare xm senza influenzare ym Se T ha una semantica immutabile, né xm né ym cambieranno mai. La semantica immutabile può essere simulata da oggetti di riferimento o di valore. Le stringhe sono oggetti di riferimento immutabili.
supercat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.