2009-07-22 15 views
8

Ho appena fatto una routine Swap in C# come questo:Sono ref e out in C# gli stessi puntatori in C++?

static void Swap(ref int x, ref int y) 
{ 
    int temp = x; 
    x = y; 
    y = temp; 
} 

fa la stessa cosa che il codice presente C++ fa:

void swap(int *d1, int *d2) 
{ 
    int temp=*d1; 
    *d1=*d2; 
    *d2=temp; 
} 

Così sono i ref e out parole chiave come punti di riferimento per C# senza utilizzando il codice unsafe?

risposta

10

Sono più limitati. Puoi dire ++ su un puntatore, ma non su un ref o out.


EDIT certa confusione nei commenti, in modo da essere assolutamente chiaro: il punto qui è quello di confrontare con le funzionalità di puntatori. Non è possibile eseguire la stessa operazione di ptr++ su un ref/out, vale a dire farlo indirizzare una posizione adiacente in memoria. È vero (ma irrilevante qui) che è possibile eseguire l'equivalente di (*ptr)++, ma sarebbe quello di confrontarlo con le capacità dei valori , non i puntatori.


E 'una scommessa sicura che essi sono internamente solo i puntatori, perché lo stack non viene spostato e C# è organizzato con cura in modo che ref e out riferiscono sempre a una regione attiva della pila.


EDIT essere assolutamente chiari nuovamente (se non era già chiaro dall'esempio sotto), il punto qui non è che ref/out può solo punto alla pila. È quello quando punta allo stack, è garantito dalle regole del linguaggio per non diventare un puntatore pendente. Questa garanzia è necessaria (e rilevante/interessante qui) perché lo stack scarta semplicemente le informazioni in conformità con le uscite di chiamata del metodo, senza controlli per garantire che esistano ancora dei referrer.

Viceversa quando ref/out riferisce agli oggetti nel mucchio GC non è una sorpresa che tali oggetti possano essere mantenuto in vita il tempo necessario: il mucchio GC è progettato proprio allo scopo di oggetti di ritegno per un certo periodo di tempo richiesto dai loro referrer e fornisce il pinning (vedi esempio sotto) per supportare situazioni in cui l'oggetto non deve essere spostato da GC compacting.


Se mai giocare con interoperabilità nel codice non sicuro, troverete che ref è strettamente correlata ai puntatori.Ad esempio, se un interfaccia COM è dichiarato così:

HRESULT Write(BYTE *pBuffer, UINT size); 

L'assembly di interoperabilità si trasformarlo in questo:

void Write(ref byte pBuffer, uint size); 

e si può fare questo a chiamarla (credo che la roba di interoperabilità COM si prende cura di pinning matrice):

byte[] b = new byte[1000]; 
obj.Write(ref b[0], b.Length); 

In altre parole, ref al primo byte si ottiene l'accesso a tutto questo; apparentemente è un puntatore al primo byte.

+0

Si può '++' su un argomento 'ref' bene, ma non significa la stessa cosa. –

+0

Inoltre, "ref" e "out" fanno sempre riferimento a una regione attiva dello stack "è completamente sbagliato. Il tuo esempio crea un 'ref' in un oggetto sull'heap gc. –

6

I parametri di riferimento in C# possono essere utilizzati per sostituire uno uso di puntatori, sì. Ma non tutto.

Un altro uso comune per i puntatori è un mezzo per iterare su un array. I parametri out/ref non possono farlo, quindi no, non sono "uguali ai puntatori".

1

La risposta breve è Sì (funzionalità simile, ma non esattamente lo stesso meccanismo). Come nota a margine, se si utilizza FxCop per analizzare il codice, utilizzando out e ref si verificherà un errore "Microsoft.Design" di "CA1045: DoNotPassTypesByReference".

1

La cosa bella dell'utilizzo di su è che è garantito che all'elemento verrà assegnato un valore - in caso contrario verrà visualizzato un errore di compilazione.

2

In realtà, li confronterei con riferimenti C++ anziché puntatori. I puntatori, in C++ e C, sono un concetto più generale e i riferimenti faranno ciò che vuoi.

Tutti questi sono indubbiamente puntatori sotto le coperte, ovviamente.

+0

Sintatticamente somigliano più a dei puntatori, perché devi anteporre 'ref', proprio come devi anteporre' & 'per ottenere un puntatore in C/C++. –

+0

(intendo al sito di chiamata.) –

+0

Quindi sono davvero da qualche parte nel mezzo (in quanto non è necessario dereferenziarlo durante la lettura/scrittura su di esso). –

3

ref e out vengono utilizzati solo con argomenti di funzione per indicare che l'argomento deve essere passato per riferimento anziché per valore. In questo senso, sì, sono un po 'come i puntatori in C++ (più come riferimenti in realtà). Maggiori informazioni a riguardo in this article.

1

Mentre i paragoni sono negli occhi di chi guarda ... Io dico di no. 'ref' modifica la convenzione di chiamata ma non il tipo dei parametri. Nell'esempio C++, d1 e d2 sono di tipo int *. In C# sono ancora in Int32, sono semplicemente passati per riferimento anziché per valore.

A proposito, il codice C++ in realtà non scambia i suoi input nel senso tradizionale. Generalizzando in questo modo:

template<typename T> 
void swap(T *d1, T *d2) 
{ 
    T temp = *d1; 
    *d1 = *d2; 
    *d2 = temp; 
} 

... non funziona a meno che non tutti i tipi di T hanno costruttori di copia, e anche allora sarà molto più inefficiente di scambio puntatori.

+0

Non penso che la tua analogia funzioni abbastanza; considera 'int *' versus 'const int *' in C++. Const è un qualificatore che limita l'uso del tipo a cui si applica, trasformandolo in un tipo diverso. Ma quando C++ è compilato in IL, 'const' è trasformato in un tipo di modificatore personalizzato su un tipo piuttosto che definire un diverso tipo nel sistema di tipo CLI. Questo dimostra che "type" nella lingua è separato da "type" nel runtime.Allo stesso modo 'ref' limita ciò che puoi fare con l'oggetto (non puoi catturarlo in un lambda, per esempio), e questo fa parte del sistema di tipi di C#, anche se non di CLI. –