2009-06-07 4 views
134

Ich verstehe, dass wenn ich einen Wert-Typ (int, struct usw.) als Parameter (ohne das Schlüsselwort ref) übergeben, eine Kopie dieser Variablen an die Methode übergeben wird, aber wenn ich das ref Schlüsselwort eine Referenz verwenden Auf diese Variable wird verzichtet, nicht auf eine neue.Was ist die Verwendung von "Ref" für Referenztyp-Variablen in C#?

Aber bei Referenztypen, wie Klassen, auch ohne das Schlüsselwort ref, wird eine Referenz an die Methode übergeben, keine Kopie. Also, was ist das ref Schlüsselwort mit Referenztypen?


Nehmen Sie zum Beispiel:

var x = new Foo(); 

Was ist der Unterschied zwischen der folgenden?

void Bar(Foo y) { 
    y.Name = "2"; 
} 

und

void Bar(ref Foo y) { 
    y.Name = "2"; 
} 

Antwort

123

Sie können ändern, was foo Punkte mit y:

Foo foo = new Foo("1"); 

void Bar(ref Foo y) 
{ 
    y = new Foo("2"); 
} 

Bar(ref foo); 
// foo.Name == "2" 
+13

so dass im Grunde Sie bekommen ein Verweis auf die Originalreferenz – lhahne

+1

Sie können ändern, worauf die ursprüngliche Referenz verweist, also ja. – user7116

+1

Chris, deine Erklärung ist großartig; Danke, dass Sie mir geholfen haben, dieses Konzept zu verstehen. –

26

Es gibt Fälle, in denen Sie tatsächlich die Referenz und nicht die anvisierten Objekts ändern möchten:

void Swap<T>(ref T x, ref T y) { 
    T t = x; 
    x = y; 
    y = t; 
} 

var test = new[] { "0", "1" }; 
Swap(ref test[0], ref test[1]); 
5

Sie können die übergebene Referenz ändern, z. durch Bezugnahme

void Bar() 
{ 
    var y = new Foo(); 
    Baz(out y); 
} 

void Baz(out Foo y) 
{ 
    // Return a new reference 
    y = new Foo(); 
} 
9

Wenn Sie einen Referenztyp mit dem Schlüsselwort ref geben, übergeben Sie die Referenz:

void Bar() 
{ 
    var y = new Foo(); 
    Baz(ref y); 
} 

void Baz(ref Foo y) 
{ 
    y.Name = "2"; 

    // Overwrite the reference 
    y = new Foo(); 
} 

Sie auch aus, wenn Sie übergeben nicht kümmern uns um die Referenz verwenden können und die Methode, die Sie aufrufen, kann dem Parameter einen neuen Wert zuweisen. Diese Änderung wird an den aufrufenden Bereich weitergegeben. Ohne ref wird die Referenz nach Wert übergeben, und das passiert nicht.

C# hat auch das 'out' Schlüsselwort, das ref sehr ähnlich ist, außer dass mit 'ref' Argumente vor dem Aufruf der Methode initialisiert werden müssen, und mit 'out' müssen Sie einen Wert in der empfangenden Methode zuweisen.

4

Eine weitere Reihe von Code

class O 
{ 
    public int prop = 0; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     O o1 = new O(); 
     o1.prop = 1; 

     O o2 = new O(); 
     o2.prop = 2; 

     o1modifier(o1); 
     o2modifier(ref o2); 

     Console.WriteLine("1 : " + o1.prop.ToString()); 
     Console.WriteLine("2 : " + o2.prop.ToString()); 
     Console.ReadLine(); 
    } 

    static void o1modifier(O o) 
    { 
     o = new O(); 
     o.prop = 3; 
    } 

    static void o2modifier(ref O o) 
    { 
     o = new O(); 
     o.prop = 4; 
    } 
} 
+2

Ausgabe: \ r \ n 1: 1 \ r \ n 2: 4 – Archie

15

Jon Skeet schrieb a great article über Parameter in C# übergeben. Es zeigt deutlich das genaue Verhalten und die Verwendung von Parametern durch Wert, Referenz (ref) und Ausgabe (out).

Hier ist ein wichtiges Zitat von dieser Seite in Bezug auf ref Parameter:

Referenzparameter nicht passieren die Werte der Variablen in der Funktion Memberaufruf verwendet - sie verwenden die Variablen selbst.Anstatt einen neuen Speicherort für Erstellen der Variable in dem Funktionselement Erklärung, die gleiche Speicherstelle verwendet wird, so dass der Wert der Variablen in dem Funktionselement und den Wert des Referenzparameters wird immer seine das Gleiche. Referenzparameter müssen der ref Modifikator als Teil sowohl der Erklärung und der Anrufung - das bedeutet, es ist immer klar, wenn man sind vorbei etwas Bezug genommen wird.

+10

Ich mag die Analogie der Weitergabe Ihrer Hunde Leine an einen Freund für die Weitergabe einer Referenz-Wert ... es bricht jedoch schnell, weil ich denke, Sie _would_ wahrscheinlich bemerkt, wenn dein Freund deinen shitzu an einen Dobermann verkauft hat, bevor er dir die Leine zurückgegeben hat ;-) – corlettk

12

Sehr schön hier erklärt: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

Auszug aus dem Artikel:

Eine Variable eines Referenztyp nicht seine Daten nicht direkt enthalten; es enthält einen Verweis auf seine Daten. Wenn Sie einen Referenztyp Parameter als Wert übergeben, ist es möglich, die Daten zu ändern, auf die die Referenz verweist, z. B. den Wert eines Klassenmembers. Sie können jedoch den Wert der Referenz nicht selbst ändern; Das heißt, Sie können nicht denselben Verweis verwenden, um Speicher für eine neue Klasse zuzuweisen, und persistieren außerhalb des Blocks. Übergeben Sie dazu den Parameter mit dem Schlüsselwort ref or out .

+4

Die Erklärung ist in der Tat sehr nett. Link-only-Antworten werden jedoch von SO abgeraten. Ich fügte eine Zusammenfassung aus dem Artikel hinzu, um den Lesern hier eine Bequemlichkeit zu bieten. – Marcel

1

Ein Parameter in einer Methode scheint immer eine Kopie zu übergeben, die Frage ist eine Kopie von was. Eine Kopie wird von einem Kopierkonstruktor für ein Objekt erstellt, und da alle Variablen Objekt in C# sind, glaube ich, dass dies für alle von ihnen der Fall ist. Variablen (Objekte) sind wie Menschen, die an einigen Adressen leben. Wir ändern entweder die Menschen, die an diesen Adressen leben, oder wir können mehr Referenzen zu den Menschen schaffen, die an diesen Adressen im Telefonbuch leben (flache Kopien). So kann sich mehr als ein Bezeichner auf dieselbe Adresse beziehen. Referenztypen wünschen mehr Platz. Anders als bei Werttypen, die durch einen Pfeil direkt mit ihrer Kennung im Stapel verbunden sind, haben sie einen Wert für eine andere Adresse im Heap (ein größerer Platz zum Verweilen). Dieser Speicherplatz muss vom Heap genommen werden.

Werttyp: Indentifier (enthält Wert = Adresse des Stapelwert) ----> Wert des Werttyp

Referenztyp: Identifier (enthält Wert = Adresse des Stapelwert) ----> (enthält Wert = Adresse des Heap-Wert) ----> Heap-Wert (am häufigsten enthält Adressen auf andere Werte), mehr Pfeile vorstellen kleben in verschiedene Richtungen Array [0], Array [1], array [2]

Die einzige Möglichkeit, einen Wert zu ändern, besteht darin, den Pfeilen zu folgen. Wenn ein Pfeil verloren geht/verändert wird, ist der Wert nicht erreichbar.

3

Zusätzlich zu den bestehenden Antworten:

Wie Sie für den Unterschied der zwei Methoden gefragt: Es gibt keine Co (ntra) Varianz bei ref oder out mit:

class Foo { } 
class FooBar : Foo { } 

static void Bar(Foo foo) { } 
static void Bar(ref Foo foo) { foo = new Foo(); } 

void Main() 
{ 
    Foo foo = null; 
    Bar(foo);   // OK 
    Bar(ref foo);  // OK 

    FooBar fooBar = null; 
    Bar(fooBar);  // OK (covariance) 
    Bar(ref fooBar); // compile time error 
}