Utilizzando REF & OUT le parole chiave con il passaggio per riferimento e passaggio per valore in C #

StackOverflow https://stackoverflow.com/questions/1683394

  •  18-09-2019
  •  | 
  •  

Domanda

Ecco quello che ho capito finora:

passaggio per valore

Passaggio dal valore significa una copia di un argomento viene passato. Modifiche a quella copia non cambiano l'originale.

PASSA PER RIFERIMENTO

Passaggio da mezzi di riferimento viene passato un riferimento all'originale. modifiche al riferimento influenzano l'originale.

REF parola chiave

REF dice al compilatore che l'oggetto viene inizializzato prima di entrare in funzione. REF intende il valore è già impostato, il metodo può quindi leggerlo e modificarlo. REF è di due modi, sia dentro che fuori.

OUT parola chiave

OUT indica al compilatore che l'oggetto verrà inizializzata all'interno della funzione. OUT significa che il valore non è già impostato, e quindi deve essere impostato prima della chiamata di ritorno. OUT è solo un modo, che è fuori.

Domanda

Quindi, in quali scenari si potrebbe combinare l'uso di l'arbitro e le parole chiave fuori, con il passaggio per riferimento o passando per valore? Esempi potrebbero aiutare moltissimo.

Guida molto apprezzato.

È stato utile?

Soluzione

Si potrebbe non combinare ref e out il 1 ° parametro. Entrambi significano 'passare riferimento'.

Ovviamente si può combinare i parametri ref e parametri fuori in un metodo.

La differenza tra ref e out sta principalmente nella intento . segnali ref trasporto dei dati a 2 vie, significa fuori 1 via.

Ma oltre l'intenzione, il compilatore C # tracce definitiva assegnazione e che fa la differenza più notevole. Inoltre impedisce l'uso improprio (lettura) di un parametro out.

void SetOne(out int x) 
{
  int y = x + 1; // error, 'x' not definitely assigned.
  x = 1;         // mandatory to assign something
}

void AddTwo(ref int x)
{
    x = x + 2;  // OK, x  is known to be assigned
}

void Main()
{
    int foo, bar;

    SetOne(out foo); // OK, foo does not have to be assigned
    AddTwo(ref foo); // OK, foo assigned by SetOne
    AddTwo(ref bar); // error, bar is unassigned
}

Altri suggerimenti

  

Guida molto apprezzato

La vostra comprensione sarà migliorata da un uso corretto e attento del linguaggio.

  

Passaggio dal valore significa una copia di un argomento viene passato.

Sì, questo è esattamente preciso.

  

Modifiche a quella copia non cambiano l'originale.

Non esattamente. Iniziare con attenzione la distinzione tra Valori e variabili . Prendere in considerazione:

class Foo { public int x; }
...
void N() 
{
  Foo blah = new Foo();
  blah.x = 0;
  M(blah);
}
...
void M(Foo foo)
{
  foo.x = 123; // changes blah.x
  foo = null; // does not change blah
}

Le variabili qui sono x, bla e foo. x è un campo, bla è un locale, foo è un parametro formale.

I valori qui sono nulli, 0, 123, e un riferimento a un'istanza di Foo. Questo riferimento è un valore. E 'fondamentale per capire questo fatto.

Una copia del valore di bla è passato copiando il valore della variabile in bla variabile pippo. Il valore di bla è un riferimento all'istanza di Foo.

M può modificare il valore della variabile x perché M ha una copia del valore di bla, che è un riferimento ad un Foo. Quando M cambia il contenuto del foo null, che non cambia bla; foo contiene una copia del valore di blah.

  

Passaggio da mezzi di riferimento viene passato un riferimento all'originale.

Scegli il tuo testo con attenzione. Che cosa è "l'originale"?

Ciò rilevare meglio come "passaggio per riferimento significa che un riferimento all'argomento, che deve essere una variabile, viene passata". Un modo più semplice per pensare a questo proposito è che "il passaggio per riferimento rende il parametro di un alias per la variabile passata come argomento".

  

modifiche al riferimento influenzano l'originale.

Dato che il parametro e l'argomento sono alias per l'altro, non è che le modifiche al riferimento influenzano l'originale; Il riferimento è l'originale. Sono entrambi la stessa variabile .

  

REF dice al compilatore che l'oggetto viene inizializzato prima di entrare in funzione.

"L'oggetto" è privo di significato. Vuoi dire che "la variabile".

"ref" non "dire al compilatore che la variabile viene inizializzata". Piuttosto, "ref" dice al compilatore "si, compilatore, necessario verificare che la variabile viene inizializzata". Che è piuttosto diverso!

  

REF significa il valore è già impostato,

No, ref richiede che il variabile è già impostato. Non c'è una cosa come "l'impostazione di un valore".

  

il metodo può quindi leggerlo e modificarlo.

Quando, in "it" si intende "la variabile".

  

REF è due modi, sia dentro che fuori.

Una corretta.

  

OUT indica al compilatore che l'oggetto verrà inizializzata all'interno della funzione.

Smettere di usare "l'oggetto" per significare "la variabile". Capirai cose molto più chiaramente se si smette di confondere cose completamente diverse. Le variabili non sono oggetti. Le variabili sono percorsi di archiviazione , alcuni dei quali potrebbe contenere valori , e alcuni di questi valori potrebbero essere i riferimenti agli oggetti .

Quindi, dice il compilatore che la variabile verrà inizializzato all'interno del metodo, sì, ma questo non è giusto. Stai dimenticando i casi in cui il metodo genererà un'eccezione, o il metodo andrà in un ciclo infinito - quelli sono anche gli scenari legali.

  

OUT significa il valore non è già impostato,

Anche in questo caso, da "il valore", si intende "la variabile". Ma questo non è esatto. E 'perfettamente legale per passare una variabile inizializzata come parametro di "fuori". Inutile, ma legale.

  

e quindi deve essere impostata prima di chiamare ritorno.

"ritorno" non si chiama; metodi sono chiamati. Ma sì, il metodo deve assegnare un valore alla variabile prima returning normalmente.

  

OUT è solo un modo, che è fuori.

A destra.

  

Quindi, in quali scenari si potrebbero combinare l'uso di REF e fuori le parole chiave

Non ci sono tali scenari.

Si capisce la dinamica del passaggio in entrambi i casi. Alcuni scenari parametro potrebbe essere:

  • ref int num per un parametro / out. La funzione possono modificare il valore in esso.
  • out int num come ref tranne funzione deve assegnare un valore ad essa prima di ritornare.

In generale parametri di uscita sono buoni per quando una funzione deve restituire più valori, perché una funzione ha un solo valore di ritorno (anche se può essere composto).

A volte i fornitori di accesso ai dati, come alcuni metodi di ADO.NET, utilizzare i parametri di output per fornire informazioni indietro. Alcuni metodi di accesso ai dati imitano stored procedure del database che hanno in / out parametri.

Modifica Una condizione per tipi di riferimento è il parametri ref StringBuilder word o StringBuilder word (in valore) comportano allo stesso - la stringa esterno è interessato, anche se l'implementatoin sottostante può differire leggermente perché a quel punto l'attenzione è il riferimento e non il valore sul mucchio.

Modifica Ho corretto questa risposta per riflettere che la parola 'out' in C # non fa quello che ci si potrebbe aspettare a (ad esempio, un vero e proprio OUT parametro nella informatica senso del termine ). Originariamente ho affermato che 'fuori' è stato passaggio per valore fuori, ma è stato dimostrato sbagliato.

Non è possibile utilizzare 'fuori' e 'ref' insieme. Ci sono tre convenzioni di chiamata in C # (NET Framework):

  • Nessun parola chiave = passaggio per valore (IN)
  • 'out' Chiave = Passo con riferimento (REF) senza necessità assegnazione definitiva prima di chiamare
  • 'ref' Chiave = Passo con riferimento (REF) con un requisito di assegnazione definitiva prima di chiamare

C # non ha alcuna vera OUT o capacità parametro IN-OUT.

Per vedere che i parametri di 'Out' in C # non sono veri parametri OUT, è possibile utilizzare il seguente codice:

  public class Test
  {
    Action _showValue;

    public void Run()
    {
      string local = "Initial";
      _showValue = () => { Console.WriteLine(local.ToString()); };

      Console.WriteLine("Passing by value");
      inMethod(local);

      Console.WriteLine("Passing by reference with 'out' keyword");
      outMethod(out local);

      Console.WriteLine("Passing by reference with 'ref' keyword");
      refMethod(ref local);

    }

    void inMethod(string arg)
    {
      _showValue();
      arg = "IN";
      _showValue();
    }

    void outMethod(out string arg)
    {
      _showValue();
      arg = "OUT";
      _showValue();
    }

    void refMethod(ref string arg)
    {
      _showValue();
      arg = "REF";
      _showValue();
    }
  }

L'output è:

Passing by value
Initial
Initial
Passing by reference with 'out' keyword
Initial
OUT
Passing by reference with 'ref' keyword
OUT
REF

Come si può vedere, sia 'fuori' e 'ref' in realtà passano da REF. L'unica differenza è nel modo in cui il compilatore li tratta a scopo di assegnazione definiti.

Uso della parola chiave OUT è utile se si dispone di un metodo che si deve restituire più di un valore. Ad esempio, guardate metodi come int.TryParse().

Utilizzando REF è più per esplicitazione di oggetti. Tenendo presente che l'eventuale non-primitivo passato in un metodo è intrinsecamente passati per riferimento, non c'è un sacco di bisogno di esso in codice gestito normale. Dichiarando la parola chiave REF si sta affermando che il parametro sarà probabilmente modificato nel corpo del metodo, e quindi il codice chiamante dovrebbe essere a conoscenza di esso (quindi il motivo per cui è necessario aggiungere esplicitamente il ref nel codice chiamante pure.

Se si capisce C ++ forse questo ti aiuterà a:

void Foo(Bar) {} // pass by value c# (if it's a value type ;))
void Foo(Bar) {} // c++

void Foo(Bar) {} // pass by reference c# (if it's a reference type ;))
void Foo(Bar&) {} // c++

void Foo(ref Bar) {} // c#
void Foo(Bar*) // c++

void Foo(out Bar) {} // c#
void Foo(Bar**) {} // c++ (the contents of *Bar needs to be filled up)

Si passa da qualcosa ref che si desidera essere letto e scritto da un'altra funzione, così si dovrebbe passare la variabile inizializzata al fine di essere letto correttamente.

EDIT: esempio:

void mymethod(ref int a) {
  a++;
}

Si passa come qualcosa che si desidera essere scritto da un'altra funzione, ma non è necessario inizializzare come non sarà letto dalla funzione, unica scritta.

EDIT: esempio:

void mymethod2(out string a) {
  a="hello";
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top