2009-04-29 8 views
18

Es gibt bereits eine Reihe von Fragen zur Definition von "ref" und "out" -Parametern, aber sie scheinen wie schlechtes Design zu sein. Gibt es Fälle, in denen Sie glauben, dass ref die richtige Lösung ist?In C#, wo verwenden Sie "Ref" vor einem Parameter?

Es scheint, als könnten Sie immer etwas anderes machen, das sauberer ist. Kann mir jemand ein Beispiel geben, wo dies die "beste" Lösung für ein Problem wäre?

Antwort

11

Meiner Meinung nach, ref weitgehend kompensiert für die Schwierigkeit der Deklaration neuer Utility-Typen und die Schwierigkeit, "Information auf" Informationen zu "tacking" auf Informationen, die Dinge, die C# hat große Schritte in Richtung Adressierung seit seiner Entstehung durch LINQ, Generika und anonyme Typen.

Also nein, ich denke nicht, dass es viele klare Anwendungsfälle mehr gibt. Ich denke, es ist weitgehend ein Relikt davon, wie die Sprache ursprünglich entworfen wurde.

Ich denke, dass es immer noch sinnvoll ist (wie oben erwähnt) in dem Fall, wenn Sie eine Art von Fehlercode aus einer Funktion sowie einen Rückgabewert zurückgeben müssen, aber nichts anderes (also ein größerer Typ isn ' Wenn ich dies in einem Projekt überall durchführe, würde ich wahrscheinlich einen generischen Wrapper-Typ für einen Ding-plus-Fehler-Code definieren, aber in jedem gegebenen Fall sind ref und out OK.

+1

Ich stimme der Idee zu, Wrapper-Typen zu erstellen, wenn es so aussieht, als würde das Konstrukt "Rückgabecode + Wert" ausgiebig in einem Projekt verwendet. –

1

Ich denke, die besten Anwendungen sind diejenigen, die Sie normalerweise sehen; Sie müssen sowohl einen Wert als auch einen "Erfolgsindikator" haben, der keine Ausnahme von einer Funktion darstellt.

+3

Aber wäre das nicht der perfekte Kandidat für' out 'Parameter' out 'scheint mir viel klarer als' ref 'und es ist auch in der BCL bevorzugt (wie in der ' .TryParse' Methoden). –

+0

So wahr.Ich muss offensichtlich jetzt schlafen, es ist spät hier ...;) –

1

Die tatsächliche Verwendung für diese ist, wenn Sie eine Struktur erstellen. Strukturen in C# sind Werttypen und werden daher immer komplett kopiert, wenn sie nach Wert übergeben werden. Wenn Sie es z. B. aus Leistungsgründen übergeben müssen oder weil die Funktion Änderungen an der Variablen vornehmen muss, verwenden Sie das Schlüsselwort ref.

Ich konnte sehen, wenn jemand eine Struktur mit 100 Werten hat (offensichtlich ein Problem bereits), würden Sie wahrscheinlich es als Referenz weitergeben wollen, um 100 Werte zu vermeiden. Das und das Zurückgeben dieser großen Struktur und des Schreibens über den alten Wert würde wahrscheinlich Leistungsprobleme haben.

+1

Es ist nicht ganz redundant im Falle eines Referenztyps, denn wenn Übergeben Sie 'Ref', kann die Funktion, die die Variable bekommt, geändert werden e Ihre Referenz, um auf eine ganz andere Stelle (oder auf null) zu zeigen. Wenn Sie "ref" nicht verwendet haben, könnte sie nur die existierende Instanz Ihrer Sache mutieren. – mquander

+0

@mquander Wäre das Ref-Schlüsselwort wirklich notwendig, um das zu tun? Ich würde denken, dass Sie das ohne das "ref" tun könnten –

+1

Diese Information ist falsch! In C# werden Referenzen standardmäßig als Wert übergeben. Siehe http://www.yoda.arachsys.com/csharp/parameters.html –

3

Was, wenn Sie wollten mehrere Objekte zurückgeben, die aus einem unbekannten Grund nicht zu einem einzigen Objekt zusammengebunden sind.

void GetXYZ(ref object x, ref object y, ref object z); 

EDIT: divo vorgeschlagenen Parameter mit OUT wäre besser geeignet für diese. Ich muss zugeben, er hat Recht. Ich werde diese Antwort hier als eine, für das Protokoll, dies ist eine unangemessene Lösung. OUT übertrumpft REF in diesem Fall.

+0

Ich glaube, in den meisten In diesen Fällen könntest du deinen Code so umgestalten, dass er ähnliche Dinge zu besseren Typen zusammenfasst oder jedes Ding auf eine klarere und hoffentlich immer noch effizientere Weise unabhängig voneinander zurückgibt. – mquander

+0

@ mquander-Stimme völlig zu. Ich denke, wenn Sie das tun, haben Sie entweder eine Methode, die vielleicht zu viel macht, oder ein schlechtes Objektmodell. Ich bin sicher, dass jemand das aus einem guten Grund getan hat. –

+4

Ich würde "out" Parameter in diesem Fall bevorzugen, weil die Absicht dann viel klarer ist. –

4

P/Invoke ist der einzige Ort, an dem ich wirklich an eine Stelle denke, wo Sie ref oder out verwenden müssen. Andere Fälle, sie können bequem sein, aber wie Sie sagten, gibt es im Allgemeinen einen anderen, saubereren Weg.

7

Ein Bereich ist bei der Verwendung von kleinen Utility-Funktionen, wie zB:

void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; } 

ich keine ‚sauberere‘ Alternativen hier sehen Sie. Zugegeben, das ist nicht gerade Architektur-Level.

0

Der offensichtliche Grund für die Verwendung des Schlüsselworts "ref" ist, wenn Sie eine Variable als Referenz übergeben möchten. Übergeben Sie beispielsweise einen Werttyp wie System.Int32 an eine Methode und ändern Sie ihren tatsächlichen Wert. Eine spezifischere Verwendung könnte sein, wenn Sie zwei Variablen austauschen möchten.

public void Swap(ref int a, ref int b) 
{ 
    ... 
} 

Der Hauptgrund für die Verwendung des Schlüsselworts "out" besteht darin, mehrere Werte aus einer Methode zurückzugeben. Persönlich bevorzuge ich es, die Werte in eine spezialisierte Struktur oder Klasse zu packen, da die Verwendung des Parameters out ziemlich hässlichen Code erzeugt. Parameter, die mit "out" übergeben werden - ist wie "ref" - als Referenz übergeben.

public void DoMagic(out int a, out int b, out int c, out int d) 
{ 
    ... 
} 
9

Nun wird ref im Allgemeinen für spezielle Fälle verwendet, aber ich würde es nicht überflüssig nennen oder eine Legacy-Funktion von C#. Sie werden es sehen (und out) verwendet viel in XNA zum Beispiel. In XNA ist eine Matrix eine struct und eine ziemlich massive dabei (ich glaube 64 Bytes) und es ist im Allgemeinen am besten, wenn Sie es an Funktionen übergeben mit ref, um das Kopieren von 64 Bytes zu vermeiden, aber nur 4 oder 8. Ein Spezialist C# -Feature? Bestimmt. Nicht mehr viel oder schlechtes Design? Ich stimme nicht zu.

1

Ein Entwurfsmuster, bei dem ref nützlich ist, ist ein bidirektionaler Besucher.

Angenommen, Sie verfügen über eine Storage Klasse, die zum Laden oder Speichern von Werten verschiedener primitiver Typen verwendet werden kann. Es ist entweder in Load Modus oder Save Modus. Es hat eine Gruppe von überladenen Methoden namens Transfer, und hier ist ein Beispiel für den Umgang mit int Werte.

public void Transfer(ref int value) 
{ 
    if (Loading) 
     value = ReadInt(); 
    else 
     WriteInt(value); 
} 

Es würde für andere primitive Typen ähnliche Verfahren sein - bool, string usw.

auf eine Klasse, dann das „übertragbar“ sein muss, können Sie eine Methode wie folgt schreiben würde:

public void TransferViaStorage(Storage s) 
{ 
    s.Transfer(ref _firstName); 
    s.Transfer(ref _lastName); 
    s.Transfer(ref _salary); 
} 

kann entweder das gleiche einzige Methode die Felder aus der Storage laden, oder die Felder in die Storage je speichern welchem ​​Modus das Storage Objekt befindet.

Wirklich, du listen nur alle Felder auf, die übertragen werden müssen, also nähert es sich der deklarativen Programmierung und nicht dem Imperativ. Das bedeutet, dass Sie nicht zwei Funktionen schreiben müssen (eine zum Lesen, eine zum Schreiben) und da das Design, das ich hier verwende, auftragsabhängig ist, ist es sehr praktisch, sicher zu sein, dass die Felder immer gelesen werden/in identischer Reihenfolge geschrieben. Der allgemeine Punkt ist, dass, wenn ein Parameter als ref markiert ist, Sie nicht wissen, ob die Methode ihn lesen oder schreiben wird, und dies ermöglicht Ihnen, Besucherklassen zu entwerfen, die in einer von zwei Richtungen funktionieren , die auf symmetrische Weise aufgerufen werden soll (dh bei der besuchten Methode muss nicht bekannt sein, in welchem ​​Richtungsmodus die Besucherklasse arbeitet).

Vergleich: + Attribute Reflection

Warum dies tun, anstatt die Felder zuzuschreiben und Reflexion unter Verwendung von automatisch das Äquivalent von TransferViaStorage zu implementieren? Weil manchmal Reflexion langsam genug ist, um ein Engpass zu sein (aber immer Profil, um sicher zu sein - es ist fast nie wahr, und Attribute sind viel näher an das Ideal der deklarativen Programmierung).

0

Es gibt einen eindeutigen Fall, in dem Sie das Schlüsselwort 'ref' verwenden müssen.Wenn das Objekt definiert ist, aber nicht außerhalb des Bereichs der Methode erstellt wurde, die Sie aufrufen möchten und die Methode, die Sie aufrufen möchten, die 'new' erstellen soll, müssen Sie 'ref' verwenden. z.B. {object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} wird nichts mit object 'a' tun, noch wird es sich entweder zur Kompilierung oder Laufzeit beschweren. Es wird einfach nichts tun. {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";} führt in 'a' ein neues Objekt mit dem Namen "dummy“zu sein. Aber wenn der 'new' bereits getan wurde, ref dann nicht erforderlich (aber funktioniert, wenn im Lieferumfang enthalten). {object a = new object(); Funct(a);} {Funct(object o) {o.name = "dummy";}