Stellen Sie sich eine Funktion vor, die zwei Werte zurückgibt. Wir können schreiben:
// Using out:
string MyFunction(string input, out int count)
// Using Tuple class:
Tuple<string, int> MyFunction(string input)
// Using struct:
MyStruct MyFunction(string input)
Welches ist die beste Vorgehensweise und warum?
struct
wieEric
erwähnt vor.Antworten:
Sie haben jeweils ihre Vor- und Nachteile.
Unsere Parameter sind schnell und kostengünstig, erfordern jedoch die Übergabe einer Variablen und die Abhängigkeit von Mutationen. Es ist fast unmöglich, einen out-Parameter mit LINQ korrekt zu verwenden.
Tupel üben Druck auf die Speicherbereinigung aus und dokumentieren sich nicht selbst. "Item1" ist nicht sehr beschreibend.
Benutzerdefinierte Strukturen können langsam kopiert werden, wenn sie groß sind, sind jedoch selbstdokumentierend und effizient, wenn sie klein sind. Es ist jedoch auch schwierig, eine ganze Reihe von benutzerdefinierten Strukturen für triviale Zwecke zu definieren.
Ich würde dazu neigen, die benutzerdefinierte Strukturlösung zu verwenden, wenn alle anderen Dinge gleich sind. Noch besser ist es jedoch , eine Funktion zu erstellen, die nur einen Wert zurückgibt . Warum geben Sie überhaupt zwei Werte zurück?
UPDATE: Beachten Sie, dass Tupel in C # 7, die sechs Jahre nach dem Schreiben dieses Artikels ausgeliefert wurden, Werttypen sind und daher weniger wahrscheinlich Sammlungsdruck erzeugen.
quelle
Zusätzlich zu den vorherigen Antworten bringt C # 7 Tupel vom Werttyp, im Gegensatz zu
System.Tuple
einem Referenztyp, und bietet auch eine verbesserte Semantik.Sie können sie weiterhin unbenannt lassen und die folgende
.Item*
Syntax verwenden:Aber was an dieser neuen Funktion wirklich mächtig ist, ist die Fähigkeit, Tupel benannt zu haben. Also könnten wir das Obige wie folgt umschreiben:
Destrukturierung wird ebenfalls unterstützt:
(string firstName, string lastName, int age) = getPerson()
quelle
Ich denke, die Antwort hängt von der Semantik der Funktion und der Beziehung zwischen den beiden Werten ab.
Beispielsweise verwenden die
TryParse
Methoden einenout
Parameter, um den analysierten Wert zu akzeptieren, und geben a zurück,bool
um anzugeben, ob die Analyse erfolgreich war oder nicht. Die beiden Werte gehören nicht wirklich zusammen, daher ist es semantisch sinnvoller und die Absicht des Codes ist leichter zu lesen, denout
Parameter zu verwenden.Wenn Ihre Funktion jedoch X / Y-Koordinaten eines Objekts auf dem Bildschirm zurückgibt, gehören die beiden Werte semantisch zusammen, und es ist besser, a zu verwenden
struct
.Ich persönlich würde es vermeiden, a
tuple
für alles zu verwenden, was für externen Code sichtbar ist, da die Syntax für das Abrufen der Mitglieder umständlich ist.quelle
out
Parameter verlassen könnennull
. Es gibt einige nullbare unveränderliche Typen da draußen.try
Muster , die mit covariant Schnittstellen arbeiten , istT TryGetValue(whatever, out bool success)
; Dieser Ansatz hätte Schnittstellen erlaubtIReadableMap<in TKey, out TValue> : IReadableMap<out TValue>
und Code zugelassen, der Instanzen vonAnimal
Instanzen zuordnen möchteCar
, um eineDictionary<Cat, ToyotaCar>
[usingTryGetValue<TKey>(TKey key, out bool success)
. Eine solche Varianz ist nicht möglich, wennTValue
sie alsref
Parameter verwendet wird.Ich werde mit dem Ansatz der Verwendung des Out-Parameters fortfahren, da Sie im zweiten Ansatz ein Objekt der Tuple-Klasse erstellen und dann einen Wert hinzufügen müssten, was meiner Meinung nach eine kostspielige Operation ist, verglichen mit der Rückgabe des Parameters value in out. Wenn Sie jedoch mehrere Werte in der Tupelklasse zurückgeben möchten (was tatsächlich nicht durch die Rückgabe eines Out-Parameters erreicht werden kann), werde ich mich für den zweiten Ansatz entscheiden.
quelle
out
. Zusätzlich gibt es einparams
Schlüsselwort, das ich nicht erwähnt habe, um die Frage klar zu halten.Sie haben keine weitere Option erwähnt, die eine benutzerdefinierte Klasse anstelle von struct enthält. Wenn den Daten eine Semantik zugeordnet ist, die von Funktionen bearbeitet werden kann, oder die Instanzgröße groß genug ist (als Faustregel> 16 Byte), kann eine benutzerdefinierte Klasse bevorzugt werden. Die Verwendung von "out" wird in der öffentlichen API nicht empfohlen, da sie mit Zeigern verknüpft ist und Verständnis für die Funktionsweise von Referenztypen erfordert.
https://msdn.microsoft.com/en-us/library/ms182131.aspx
Tupel ist gut für den internen Gebrauch, aber die Verwendung in öffentlichen APIs ist umständlich. Ich stimme also zwischen Struktur und Klasse für die öffentliche API.
quelle
Es gibt keine "Best Practice". Es ist das, womit Sie sich wohl fühlen und was in Ihrer Situation am besten funktioniert. Solange Sie damit einverstanden sind, gibt es kein Problem mit einer der von Ihnen veröffentlichten Lösungen.
quelle