2008-10-09 13 views
204

Wenn ich ein Objekt an eine Methode übergebe, warum sollte ich das Schlüsselwort ref verwenden? Ist das nicht das Standardverhalten?Warum verwenden Sie das Schlüsselwort 'ref', wenn Sie ein Objekt übergeben?

Zum Beispiel:

class Program 
{ 
    static void Main(string[] args) 
    { 
     TestRef t = new TestRef(); 
     t.Something = "Foo"; 

     DoSomething(t); 
     Console.WriteLine(t.Something); 
    } 

    static public void DoSomething(TestRef t) 
    { 
     t.Something = "Bar"; 
    } 
} 


public class TestRef 
{ 
    public string Something { get; set; } 
} 

Der Ausgang ist "Bar", was bedeutet, dass das Objekt als Referenz übergeben wurde.

Antwort

209

Pass eine ref wenn Sie ändern möchten, was das Objekt ist:

TestRef t = new TestRef(); 
t.Something = "Foo"; 
DoSomething(ref t); 

void DoSomething(ref TestRef t) 
{ 
    t = new TestRef(); 
    t.Something = "Not just a changed t, but a completely different TestRef object"; 
} 

Nach DoSomething Aufruf nicht t nicht auf die ursprüngliche new TestRef beziehen, sondern bezieht sich auf ein ganz anderes Objekt.

Dies kann auch nützlich sein, wenn Sie den Wert eines unveränderlichen Objekts, z. a string. Sie können den Wert eines string nicht ändern, nachdem es erstellt wurde. Wenn Sie jedoch eine ref verwenden, können Sie eine Funktion erstellen, die die Zeichenfolge für eine andere ändert, die einen anderen Wert hat.

Edit: Wie andere Leute erwähnt haben. Es ist keine gute Idee, ref zu verwenden, wenn es nicht benötigt wird. Mit ref gibt die Methode Freiheit, das Argument für etwas anderes zu ändern, müssen Aufrufer der Methode codiert werden, um sicherzustellen, dass sie diese Möglichkeit behandeln.

Wenn der Parametertyp ein Objekt ist, fungieren Objektvariablen immer als Referenzen auf das Objekt. Das heißt, wenn das Schlüsselwort ref verwendet wird, haben Sie einen Verweis auf eine Referenz. Dies ermöglicht Ihnen, Dinge zu tun, wie im obigen Beispiel beschrieben. Aber, wenn der Parametertyp ein Grundwert (zB int) ist, dann, wenn dieser Parameter auf innerhalb der Methode zugeordnet ist, wird der Wert des Arguments, die übergeben wurde in wird nach der Methode zurück geändert werden:

int x = 1; 
Change(ref x); 
Debug.Assert(x == 5); 
WillNotChange(x); 
Debug.Assert(x == 5); // Note: x doesn't become 10 

void Change(ref int x) 
{ 
    x = 5; 
} 

void WillNotChange(int x) 
{ 
    x = 10; 
} 
1

Wenn Sie einen Wert übergeben, sind die Dinge jedoch anders. Sie können erzwingen, dass ein Wert als Referenz übergeben wird. So können Sie beispielsweise eine ganze Zahl an eine Methode übergeben und die Methode die ganze Zahl für Sie ändern lassen.

+4

Unabhängig davon, ob Sie einen Verweis oder einen Werttyp übergeben, wird das Standardverhalten als Wert übergeben. Sie müssen nur verstehen, dass bei Referenztypen der übergebene Wert * eine Referenz ist. Dies ist nicht das Gleiche wie die Übergabe * durch * Verweis. –

13

Mit ref Sie schreiben können:

static public void DoSomething(ref TestRef t) 
{ 
    t = new TestRef(); 
} 

Und t wird geändert, nachdem das Verfahren abgeschlossen ist.

16

Da es sich bei TestRef um eine Klasse handelt (die Referenzobjekte sind), können Sie die Inhalte in t ändern, ohne sie als ref übergeben zu müssen. Wenn Sie jedoch t als Referenz übergeben, kann TestRef ändern, worauf sich das Original bezieht. d. h. es muss auf ein anderes Objekt zeigen.

3

Mit dem Schlüsselwort ref mit Referenztypen übergeben Sie effektiv einen Verweis auf die Referenz. In vielerlei Hinsicht ist es dasselbe wie die Verwendung des Schlüsselwortes out, aber mit dem geringfügigen Unterschied, dass es keine Garantie dafür gibt, dass die Methode tatsächlich dem Parameter 'ed' ref einen Wert zuweist.

3

Dies ist wie ein Zeiger auf einen Zeiger in C übergeben. In .NET können Sie ändern, was das Original T verweist, persönlich obwohl ich denke, wenn Sie das tun in .NET haben Sie wahrscheinlich ein Design-Problem!

1

Ref gibt an, ob die Funktion das Objekt selbst oder nur seinen Wert erreichen kann.

Die Weitergabe als Referenz ist nicht an eine Sprache gebunden; es ist eine Parameterbindungsstrategie neben Pass-by-Value, Pass-by-Name, Pass-by-Wert usw.

Eine Randnotiz: der Klassenname TestRef ist eine schrecklich schlechte Wahl in diesem Zusammenhang;).

69

Sie müssen zwischen "Übergabe einer Referenz nach Wert" und "Übergabe eines Parameters/Arguments durch Referenz" unterscheiden.

Ich habe eine reasonably long article on the subject geschrieben zu vermeiden, sorgfältig jedes Mal das kommt auf Newsgroups schreiben :)

+1

Nun, ich habe das Problem beim Upgrade von VB6 in .Net C# -Code festgestellt. Es gibt Funktions-/Methodensignaturen, die Ref-, Out- und Plain-Parameter verwenden. Wie können wir den Unterschied zwischen einem einfachen Param und einem Ref besser unterscheiden? – bonCodigo

+1

@bonCodigo: Nicht sicher, was Sie mit "besser unterscheiden" meinen - es ist Teil der Signatur, und Sie müssen 'ref' auch auf der Aufrufseite angeben ... wo sonst würden Sie wollen, dass es ausgezeichnet wird? Die Semantik ist auch ziemlich klar, muss aber sorgfältig ausgedrückt werden (anstatt "Objekte werden durch Referenz weitergegeben", was die übliche Vereinfachung ist). –

+0

ich weiß nicht, warum Visual Studio immer noch nicht explizit zeigt, was passiert ist – MonsterMMORPG

21

In .NET, wenn Sie alle Parameter an eine Methode übergeben, wird eine Kopie erstellt wird. In Werttypen bedeutet, dass alle Änderungen, die Sie an dem Wert vornehmen, im Methodenbereich liegen und beim Beenden der Methode verloren gehen.

Beim Übergeben eines Referenztyps wird auch eine Kopie erstellt, aber es ist eine Kopie einer Referenz, d. H. Sie haben jetzt ZWEI Referenzen im Speicher für dasselbe Objekt. Wenn Sie also den Verweis zum Ändern des Objekts verwenden, wird es geändert. Wenn Sie jedoch die Referenz selbst ändern - wir müssen uns daran erinnern, dass es sich um eine Kopie handelt -, gehen alle Änderungen auch beim Beenden der Methode verloren.

Da die Menschen vor gesagt haben, ist eine Zuweisung eine Modifikation der Referenz, so ist verloren:

public void Method1(object obj) { 
obj = new Object(); 
} 

public void Method2(object obj) { 
obj = _privateObject; 
} 

Die Verfahren, die oben nicht das ursprüngliche Objekt nicht modifiziert. nur für zwei Bereiche

Eine kleine Änderung Ihrer Beispiel

using System; 

    class Program 
     { 
      static void Main(string[] args) 
      { 
       TestRef t = new TestRef(); 
       t.Something = "Foo"; 

       DoSomething(t); 
       Console.WriteLine(t.Something); 

      } 

      static public void DoSomething(TestRef t) 
      { 
       t = new TestRef(); 
       t.Something = "Bar"; 
      } 
     } 



    public class TestRef 
    { 
    private string s; 
     public string Something 
     { 
      get {return s;} 
      set { s = value; } 
     } 
    } 
+1

Ich mag diese Antwort besser als die angenommene Antwort. Es wird deutlicher, was passiert, wenn eine Referenztypvariable mit dem Schlüsselwort ref übergeben wird. Vielen Dank! – Stefan

3

ref imitiert (oder verhält) als globaler Bereich:

  • Caller
  • Callee.
6

Denken von Variablen (z.B. foo) von Referenztypen (z.B. List<T>) als Halt Objektbezeichner der Form "Object # 24601". Angenommen, die Anweisung foo = new List<int> {1,5,7,9}; bewirkt, dass foo "Object # 24601" (eine Liste mit vier Elementen) enthält. Dann foo.Length Aufruf fragt nach seiner Länge # 24601 Objekt, und es wird 4 reagieren, so foo.Length wird gleich 4.

Wenn foo an eine Methode übergeben wird, ohne ref zu verwenden, könnte das Verfahren Änderungen vornehmen # 24601 Objekt. Als Konsequenz solcher Änderungen ist foo.Length möglicherweise nicht mehr gleich 4. Die Methode selbst wird jedoch nicht in der Lage sein, foo zu ändern, was weiterhin "Objekt # 24601" enthält.

Wenn foo als ref Parameter übergeben wird, kann die aufgerufene Methode Änderungen nicht nur an Objekt # 24601, sondern auch an foo selbst vornehmen. Die Methode erstellt möglicherweise ein neues Objekt Nr. 8675309 und speichert einen Verweis darauf in foo. Wenn dies der Fall ist, würde foo nicht mehr "Objekt Nr. 24601", sondern "Objekt Nr. 8675309" enthalten.

In der Praxis enthalten Variablen vom Referenztyp keine Zeichenfolgen in der Form "Objekt Nr. 8675309"; Sie enthalten nicht einmal etwas, was sich sinnvoll in eine Zahl umwandeln lässt. Obwohl jede Referenzvariable ein Bitmuster enthält, gibt es keine feste Beziehung zwischen den Bitmustern, die in solchen Variablen gespeichert sind, und den Objekten, die sie identifizieren. Es gibt keine Möglichkeit, Code aus einem Objekt oder einer Referenz darauf zu extrahieren und später festzustellen, ob eine andere Referenz das gleiche Objekt identifiziert hat, es sei denn, der Code enthielt oder kannte eine Referenz, die das ursprüngliche Objekt identifizierte.