Qual è la differenza tra ref e out in runtime?


15

C # fornisce il refe la outparola chiave per rendere gli argomenti da passare per riferimento. La semantica dei due è molto simile. L'unica differenza è nell'inizializzazione della variabile svasata:

  • refrichiede l'inizializzazione della variabile prima di passarla alla funzione, outno.
  • outrichiede l'inizializzazione della variabile all'interno della funzione, refno.

Anche i casi d'uso di queste due parole chiave sono quasi gli stessi, e il loro uso troppo frequente è che io credo sia considerato un odore di codice (anche se ci sono casi d'uso validi come i modelli TryParsee TryGetValue).

Per questo motivo qualcuno potrebbe spiegare, perché ci sono due strumenti molto simili in C # per casi d'uso così ristretti?

Inoltre, su MSDN , si afferma che hanno un comportamento di runtime diverso:

Sebbene le parole chiave ref e out causino comportamenti di runtime diversi, non sono considerate parte della firma del metodo al momento della compilazione.

In cosa differisce il loro comportamento in fase di runtime?

Conclusione

Entrambe le risposte sembrano corrette, grazie ad entrambi. Ho accettato jmoreno perché è più esplicito.


La mia ipotesi è che entrambi siano implementati allo stesso modo di refin C ++; puntatori al puntatore oggetto (o primitivo) in questione. cioè Int32.TryParse(myStr, out myInt)(C #) viene "eseguito" allo stesso modo di int32_tryParse(myStr, &myInt)(C); l'unica differenza sono alcuni vincoli imposti dal compilatore per prevenire i bug. (Non lo pubblicherò come risposta perché potrei sbagliarmi su come funziona dietro le quinte, ma è così che immagino che funzioni [perché ha senso])
Cole Johnson

Risposte:


10

Al momento in cui è stata posta questa domanda, l'articolo MSDN era leggermente errato (da allora è stato corretto ). Invece di "causare comportamenti diversi" dovrebbe essere "richiede comportamenti diversi".

In particolare, il compilatore applica requisiti diversi per le due parole chiave, anche se lo stesso meccanismo (IL) viene utilizzato per abilitare il comportamento.


Quindi la mia comprensione è che il compilatore controlla cose come dereferenziare un outparametro o non assegnare un refparametro, ma l'IL che emette è solo un riferimento in entrambi i modi. È corretto?
Andrew,

A proposito, si potrebbe dire questo dal fatto che out Te ref Tsono resi in modo identico in altri linguaggi CLI come VB.Net o C ++ / CLI.
ach

@AndrewPiliser: corretto. Supponendo che il codice venga compilato in entrambi i modi (non si tenta di dereference quando non si dovrebbe), l'IL sarebbe identico.
jmoreno,

4

ecco un interessante commento sull'argomento che potrebbe rispondere alla tua domanda:

http://www.dotnetperls.com/ref

punto d'interesse:

"La differenza tra ref e out non è nel Common Language Runtime, ma nel linguaggio C # stesso."

aggiornare:

lo sviluppatore senior del mio lavoro ha appena confermato la risposta di @ jmoreno, quindi assicurati di leggerlo !.


Quindi, se capisco correttamente, non vi è alcuna differenza a livello di IL, il che implica che non possono avere comportamenti di runtime diversi. Ciò sarebbe in contraddizione con quanto afferma MSDN. Articolo interessante comunque!
Gábor Angyal,

@ GáborAngyal per caso non hai un link all'articolo MSDN per caso, vero? Potrebbe essere bello averlo qui se le opinioni differiscono
Dan Beaulieu,

Ecco qua: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx , ma è stato sempre nella mia domanda :)
Gábor Angyal

1
Cosa stringc'entra con tutto questo?
Robert Harvey,

È solo una parafrasi dell'articolo. Aggiornato per rimuovere la seconda linea ..
Dan Beaulieu,

2

Runtime ? Assolutamente nessuno. Non è possibile sovraccaricare un metodo con gli stessi parametri che differiscono solo per la parola chiave ref o out.

Prova a compilare questo e otterrai un errore di compilazione "Il metodo con la stessa firma è già stato dichiarato":

    private class MyClass
    {
        private void DoSomething(out int param)
        {
        }
        private void DoSomething(ref int param)
        {
        }
    }

Per rispondere a questa domanda: "... perché ci sono due strumenti molto simili in C # per casi d'uso così ristretti?"

Dal punto di vista della leggibilità del codice e dell'API c'è un'enorme differenza. Come consumatore dell'API, so che quando si utilizza "out" l'API non dipende dal parametro out. Come sviluppatore API preferisco "out" e utilizzo "ref" solo quando assolutamente (RARAMENTE!) Necessario. Vedi questo riferimento per una grande discussione:

/programming/1516876/when-to-use-ref-vs-out

Informazioni di supporto: ho compilato il seguente metodo e lo ho smontato. Ho usato le parole chiave ref e out (in questo esempio), ma il codice assembly non è cambiato tranne per un riferimento di indirizzo come mi aspetterei:

    private class MyClass
    {
        internal void DoSomething(out int param)
        {
            param = 0;
        }
    }

00000000 push ebp
00000001 mov ebp, esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp, 34h
00000009 xor eax, eax
0000000b mov dword ptr [ebp-10h], eax
0000000e mov dword ptr [ebp-100], eax
0000 mov dword ptr [ebp-3Ch], ecx
00000014 mov dword ptr [ebp-40h], edx
00000017 cmp dword ptr ds: [008B1710h], 0
0000001e je 00000025
00000020 call 6E6B601E
00000025 nop
param = 0;
00000026 mov eax, dword ptr [ebp-40h]
00000029 xor edx, edx
0000002b mov dword ptr [eax], edx
}
0000002d nop
0000002e lea esp, [ebp-0Ch]
00000031 pop ebx
00000032 pop esi
00000033 pop edi
00000034 pop ebp
00000035 ret

Sto leggendo correttamente l'assemblea?


Quello che scrivi è corretto, ma tieni presente che non è evidente che non ci siano differenze di runtime perché questo sovraccarico non è permesso.
Gábor Angyal,

Ahh - buon punto! Qualcuno può disassemblare il codice sopra e dimostrarmi giusto o sbagliato? Sono terribile a leggere l'assemblea, ma curioso di saperlo.
Shmoken,

Okay - Ho smontato il codice usando "out" e "ref" e non vedo alcuna differenza per il mio metodo DoSomething:
Shmoken,
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.