2009-07-22 12 views
8

Ich habe gerade eine Swap-Routine in C# wie folgt aus:Sind ref und out in C# die gleichen Zeiger in C++?

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

Es ist die gleiche Sache, dass diese C++ Code tut:

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

So sind die ref und out Schlüsselwörter wie Zeiger für C#, ohne mit unsafe code?

Antwort

10

Sie sind begrenzter. Sie können ++ auf einem Zeiger, aber nicht auf einem ref oder out sagen.


EDIT Einige Verwirrung in den Kommentaren, so absolut klar zu sein: Der Punkt hier ist mit den Fähigkeiten von Zeigern zu vergleichen. Sie können nicht die gleiche Operation wie ptr++ an einem ref/out ausführen, d. H. Es adressieren einen benachbarten Ort im Speicher. Es ist wahr (aber irrelevant hier), dass Sie das Äquivalent von (*ptr)++ ausführen können, aber das wäre, es mit den Fähigkeiten Werte, nicht Zeiger zu vergleichen.


Es ist eine sichere Wette, dass sie intern sind nur Zeiger, weil der Stapel nicht bewegt bekommen hat und C# organisiert ist sorgfältig, so dass ref und out immer zu einem aktiven Bereich des Stapels beziehen.


EDIT Um absolut klar zu sein, wieder (wenn es nicht bereits aus dem Beispiel unten ist), hier der Punkt ist, dass nicht ref/out kann nur Punkt auf den Stapel. Es ist das , wenn es auf den Stapel zeigt, ist es durch die Sprachregeln garantiert, nicht zu einem dangling Zeiger zu werden. Diese Garantie ist notwendig (und relevant/interessant hier), da der Stapel Informationen einfach in Übereinstimmung mit Methodenaufruf-Exits verwirft, ohne dass überprüft wird, ob noch Verweiser vorhanden sind.

Umgekehrt, wenn sich ref/out auf Objekte im GC-Heap bezieht, ist es nicht verwunderlich, dass diese Objekte so lange am Leben gehalten werden können: Der GC-Heap ist genau dafür ausgelegt, Objekte für beliebig lange Zeit zu halten Sie werden von ihren Empfängern benötigt und bieten Pinning (siehe Beispiel unten), um Situationen zu unterstützen, in denen das Objekt nicht durch GC-Komprimierung verschoben werden darf.


Wenn Sie jemals mit Interop in unsicherem Code spielen, werden Sie feststellen, dass ref ist sehr eng mit Zeigern verwendet.Zum Beispiel wird, wenn eine COM-Schnittstelle wie folgt erklärt:

HRESULT Write(BYTE *pBuffer, UINT size); 

Die Interopassembly es in diese verwandeln:

void Write(ref byte pBuffer, uint size); 

Und Sie können dies nennen es tun (ich die COM-Interop Sachen glauben kümmert sich um das Array von Pinning):

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

Mit anderen Worten, wird Sie die ganze es ref zum ersten Byte zuzugreifen; es ist anscheinend ein Zeiger auf das erste Byte.

+0

Sie können '++' auf ein 'ref' Argument gut, aber es bedeutet nicht das Gleiche. –

+0

Auch "ref" und "out" beziehen sich immer auf eine aktive Region des Stapels "ist einfach völlig falsch. In Ihrem eigenen Beispiel wird ein Verweis auf ein Objekt im GC-Heap erstellt. –

6

Referenzparameter in C# können verwendet werden, um einen Verwendung von Zeigern zu ersetzen, ja. Aber nicht alles.

Eine andere häufige Verwendung für Zeiger ist als Mittel zum Iterieren über ein Array. Out/ref-Parameter können das nicht, also nein, sie sind nicht "die gleichen wie Zeiger".

1

Die kurze Antwort ist Ja (ähnliche Funktionalität, aber nicht genau der gleiche Mechanismus). Als eine Randnotiz, wenn Sie FxCop verwenden, um Ihren Code zu analysieren, führt die Verwendung von out und ref zu einem "Microsoft.Design" -Fehler von "CA1045: DoNotPassTypesByReference".

1

Das schöne an der Verwendung von aus ist, dass Sie garantiert, dass das Element einen Wert zugeordnet wird - Sie erhalten einen Kompilierungsfehler, wenn nicht.

2

Eigentlich würde ich sie mit C++ Referenzen anstelle von Zeigern vergleichen. Pointers, in C++ und C, sind ein allgemeineres Konzept und Referenzen werden tun, was Sie wollen.

Alle diese sind natürlich zweifellos Hinweise unter der Decke, natürlich.

+0

Syntaktisch ähneln sie tatsächlich eher den Zeigern, weil Sie 'ref' voraussetzen müssen, genauso wie Sie' & 'vorsetzen müssen, um einen Zeiger in C/C++ zu erhalten. –

+0

(ich meine an der rufenden Seite.) –

+0

Also sind sie wirklich irgendwo in der Mitte (wie Sie es beim Lesen/Schreiben nicht dereferenzieren müssen). –

3

ref und out werden nur mit Funktionsargumenten verwendet, um anzuzeigen, dass das Argument als Verweis anstelle von Wert übergeben werden soll. In diesem Sinne, ja, sie sind etwas wie Zeiger in C++ (eher wie Referenzen tatsächlich). Lesen Sie mehr darüber in this article.

1

Während Vergleiche im Auge des Betrachters sind ... sage ich nein. 'ref' ändert die Aufrufkonvention, aber nicht die Typ der Parameter. In Ihrem C++ - Beispiel haben d1 und d2 den Typ int *. In C# sind sie immer noch Int32, sie werden einfach als Referenz und nicht als Wert übergeben.

Übrigens tauscht Ihr C++ Code seine Eingaben nicht im herkömmlichen Sinn. Verallgemeinert man es etwa so:

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

... wird nicht funktionieren, wenn nicht alle Arten T Kopierkonstruktoren haben, und selbst dann wird viel ineffizienter als Zeiger tauschen.

+0

Ich glaube nicht, dass Ihre Analogie funktioniert; Betrachte 'int *' gegen 'const int *' in C++. Const ist ein Qualifikationsmerkmal, das die Verwendung des Typs, auf den es angewendet wird, einschränkt und ihn effektiv in einen anderen Typ umwandelt. Wenn C++ jedoch zu IL kompiliert wird, wird "const" in eine Art benutzerdefinierter Modifikator für einen Typ umgewandelt, anstatt einen anderen Typ im CLI-Typsystem zu definieren. Dies zeigt, dass "type" in der Sprache von "type" in der Laufzeit getrennt ist.Auf die gleiche Weise beschränkt 'ref' das, was Sie mit dem Objekt tun können (Sie können es beispielsweise nicht in einem Lambda erfassen), und dies ist Teil des C# -Typsystems, jedoch nicht von CLIs. –