Wann und warum solltest du void verwenden (anstelle von zB bool / int)

30

Gelegentlich stoße ich auf Methoden, bei denen ein Entwickler etwas zurückgibt, das für die Funktion nicht kritisch ist. Ich meine, wenn ich mir den Code ansehe, funktioniert er anscheinend genauso gut wie ein voidund nach einem Moment des Nachdenkens frage ich: "Warum?" Kommt Ihnen das bekannt vor?

Manchmal stimme ich zu, dass es am häufigsten besser ist, etwas wie a booloder zurückzugeben int, als einfach a zu tun void. Im Großen und Ganzen bin ich mir jedoch nicht sicher, was die Vor- und Nachteile angeht.

Je nach Situation kann das Zurückgeben von an intden Aufrufer auf die Anzahl der Zeilen oder Objekte aufmerksam machen, die von der Methode betroffen sind (z. B. 5 in MSSQL gespeicherte Datensätze). Wenn eine Methode wie "InsertSomething" einen Booleschen Wert zurückgibt, kann die Methode so entworfen werden, dass sie truebei Erfolg zurückgibt , ansonsten false. Der Anrufer kann anhand dieser Informationen entscheiden, ob er handeln möchte oder nicht.

Auf der anderen Seite,

  • Kann dies zu einem weniger eindeutigen Zweck eines Methodenaufrufs führen? Schlechte Codierung zwingt mich oft, den Methodeninhalt zu überprüfen. Wenn es etwas gibt, es sagt Ihnen , dass die Methode von einer Art ist Sie hat etwas mit dem zurückgegebenen Ergebnis zu tun.
  • Ein anderes Problem wäre, wenn Ihnen die Implementierung der Methode unbekannt ist, was hat der Entwickler beschlossen, zurückzugeben, das nicht funktionskritisch ist? Natürlich können Sie es kommentieren.
  • Der Rückgabewert muss verarbeitet werden, wenn die Verarbeitung in der schließenden Klammer der Methode beendet werden konnte.
  • Was passiert unter der Haube? Wurde die aufgerufene Methode falseaufgrund eines ausgelösten Fehlers abgerufen ? Oder hat es aufgrund des ausgewerteten Ergebnisses falsch zurückgegeben?

Welche Erfahrungen haben Sie damit gemacht? Wie würden Sie darauf reagieren?

Unabhängig
quelle
1
Eine Funktion, die ungültig zurückgibt, ist technisch gesehen überhaupt keine Funktion. Es ist nur eine Methode / Prozedur. Wenn Sie voidmindestens zurückgeben, wird der Entwickler darüber informiert, dass der Rückgabewert der Methode nicht relevant ist. Es wird eine Aktion ausgeführt, anstatt einen Wert zu berechnen.
KChaloux,

Antworten:

32

Im Fall eines bool-Rückgabewerts, der den Erfolg oder Misserfolg einer Methode angibt, bevorzuge ich das Try-prefix-Paradigma, das in verschiedenen .NET-Methoden verwendet wird.

Eine void InsertRow()Methode könnte beispielsweise eine Ausnahme auslösen, wenn bereits eine Zeile mit demselben Schlüssel vorhanden ist. Die Frage ist, ist es vernünftig anzunehmen, dass der Aufrufer sicherstellt, dass seine Zeile eindeutig ist, bevor er InsertRow aufruft? Wenn die Antwort nein ist, würde ich auch eine bool TryInsertRow()angeben, die false zurückgibt, wenn die Zeile bereits vorhanden ist. In anderen Fällen, z. B. bei Datenbankverbindungsfehlern, kann TryInsertRow dennoch eine Ausnahme auslösen, sofern die Aufrechterhaltung der Datenbankverbindung in der Verantwortung des Aufrufers liegt.

Luftpolsterfolie
quelle
4
+1 für die Unterscheidung zwischen dem Umgang mit erwarteten und unerwarteten Fehlern - meine eigene Antwort hat dies nicht genug betont.
Péter Török
Absolut eine +1 für den Versuch, ein Prinzip für TryXX-Methoden zu erfinden, das scheinbar sowohl die try-Methode als auch die main-Methode einfacher zu pflegen und ihren Zweck besser zu verstehen macht.
Unabhängige
+1 Gut gesagt. Der Schlüssel zur besten Antwort auf diese Frage ist, dass Sie dem Aufrufer manchmal die Option einer durchsetzungsfähigen Methode geben möchten, die eine Ausnahme auslöst. In diesen Fällen sollte die Ausnahme jedoch nicht von der Assertive-Methode abgefangen und gefälscht werden. Der Punkt ist, dass der Anrufer verantwortlich gemacht wird. Andererseits sollte ein Try-Paradigma (oder eine Monade) Ausnahmen abfangen und behandeln und dann entweder einen Bool oder eine beschreibende Enumeration zurückgeben, wenn weitere Details erforderlich sind. Beachten Sie, dass ein Anrufer erwartet, dass Try / Monad NIEMALS eine Ausnahme auslöst. Es ist wie ein Einstiegspunkt.
Suamere
Da erwartet wird, dass von einer Try / Monad-Ausnahme niemals eine Ausnahme auftritt, ist ein Bool jedoch nicht beschreibend genug, um den Aufrufer darüber zu informieren, dass eine Datenbankverbindung fehlgeschlagen ist, oder eine http-Anforderung einen von vielen Rückgabewerten aufweist, die für eine andere Vorgehensweise erforderlich sind. Sie können ein Enum anstelle eines Bool für Ihre Try / Monad verwenden.
Suamere
Wenn Sie TryInsertRow haben - einen Bool zurückgeben - wenn beispielsweise mehr als eine eindeutige Einschränkung in der Datenbank vorhanden ist - woher wissen Sie genau, was der Fehler war - und teilen Sie dies dem Benutzer mit? Sollte ein umfangreicherer Rückgabetyp verwendet werden, der die Fehlermeldung / den Code und den Erfolgsbool enthält?
Niico
28

IMHO, das einen "Statuscode" zurückgibt, stammt aus historischen Zeiten, bevor Ausnahmen in Sprachen mittlerer bis höherer Ebene wie C # üblich wurden. Heutzutage ist es besser, eine Ausnahme auszulösen, wenn ein unerwarteter Fehler den Erfolg Ihrer Methode verhinderte. Auf diese Weise bleibt der Fehler nicht unbemerkt, und der Anrufer kann ihn entsprechend behandeln. In diesen Fällen ist es also völlig in Ordnung, wenn eine Methode voidanstelle eines Booleschen Statusflags oder eines ganzzahligen Statuscodes nichts (dh ) zurückgibt. (Natürlich sollte dokumentiert werden, welche Ausnahmen die Methode wann auslösen darf.)

Wenn es sich hingegen um eine Funktion im engeren Sinne handelt, dh eine Berechnung auf der Grundlage von Eingabeparametern durchführt und erwartet wird, dass das Ergebnis dieser Berechnung zurückgegeben wird, liegt der Rückgabetyp auf der Hand.

Zwischen den beiden befindet sich ein grauer Bereich, in dem möglicherweise "zusätzliche" Informationen von der Methode zurückgegeben werden, wenn dies für ihre Aufrufer als nützlich erachtet wird. Wie in Ihrem Beispiel über die Anzahl der betroffenen Zeilen in einer Einfügung. Für eine Methode zum Einfügen eines Elements in eine assoziative Auflistung kann es hilfreich sein, den vorherigen Wert zurückzugeben, der diesem Schlüssel zugeordnet ist, sofern vorhanden. Solche Verwendungen können durch sorgfältige Analyse der (bekannten und erwarteten) Verwendungsszenarien einer API identifiziert werden.

Péter Török
quelle
4
+1 für Ausnahmen über den Statuscode. Die Ausnahme (schlechtes Wortspiel beabsichtigt) ist das gut verwendete TryXX-Muster.
Andy Lowry
éter ich bin bei dir da TcF und Fehler nicht zum Schweigen bringen! Wahrscheinlich aber die Grauzone. Es kann immer von Nutzen sein, etwas zurückzugeben. Betroffene Zeilen, die zuletzt eingefügte ID, eine PID der ausgeführten Software, aber ich denke, es ist einfacher, bei der Entwicklung "verdammt ja, ich gebe das zurück" zu denken. Dann, ein Jahr später beim Erweitern, "Was? Das macht ein ´int someThing = RunProcess (x) ', was passiert, wenn es nicht ausgeführt werden kann?"
Unabhängige
+1 zusätzliche Informationen ist, warum ich Methoden wie diese in höheren Sprachen wie C # codiere. Dies macht es einfach, die zusätzlichen Informationen zu verwenden, wenn Sie sie benötigen.
ashes999
2
Wow, deine eigenen Worte sind so widersprüchlich und falsch. Sie sollten niemals Ausnahmen für den Kontrollfluss oder die Kommunikation verwenden. Sie sind unendlich teurer als eine Bedingung. Wann ist die Zeit "bevor Ausnahmen alltäglich wurden"? ... Sie meinen, bevor Try / Catch-Blöcke alltäglich wurden? Ausnahmen sind seit jeher üblich. "Eine Ausnahme auslösen, wenn ein unerwarteter Fehler auftritt ..." Wie können Sie Maßnahmen ergreifen, wenn ein unerwarteter Fehler auftritt? Warum sollten Sie eine Ausnahme abfangen und sie dann erneut auslösen? Das ist so teuer. Warum sagst du "Fehler"? Fehler und Ausnahmen sind verschiedene Dinge.
Suamere
1
Und wer sagt, dass eine geworfene Ausnahme nicht unbemerkt bleibt? Nichts zwingt jemanden, sich in einen catch-Block einzuloggen. Warum nicht in einen "then" -Block? Warum "dokumentieren, welche Ausnahmen die Methode auslösen darf und wann"? Wie wäre es, wenn wir selbstdokumentierenden Code schreiben und eine Methode einfach eine Aufzählung mit den erwarteten Endzuständen dieser Methode zurückgibt? Wenn eine mögliche Ausnahme durch Überprüfung des Zustands vermieden werden kann, sollten sie gefangen und behandelt werden, ohne geworfen zu werden.
Suamere
14

Peters Antwort deckt die Ausnahmen gut ab. Weitere Überlegungen betreffen die Trennung von Befehlen und Abfragen

Nach dem CQS-Prinzip sollte eine Methode entweder ein Befehl oder eine Abfrage sein und nicht beides. Befehle sollten niemals einen Wert zurückgeben, nur den Status ändern, und Abfragen sollten nur den Status zurückgeben und nicht den Status ändern. Dies hält die Semantik sehr klar und hilft, den Code lesbarer und wartbarer zu machen.

Es gibt einige Fälle, in denen ein Verstoß gegen das CQS-Prinzip angezeigt ist. Hierbei handelt es sich normalerweise um Leistung oder Thread-Sicherheit.

Andy Lowry
quelle
1
-1 Falsch und Falsch. Erstens liegt der springende Punkt von CQS in der Frage. Ein Anrufer sollte in der Lage sein, Berechnungen oder externe Anrufe über einen statusbehafteten Dienst zu erhalten, ohne befürchten zu müssen, den Status dieses Dienstes zu ändern. Auch wenn CQS ein hilfreiches Prinzip ist, dem man locker folgen kann, wird es nur in bestimmten Designs strikt befolgt. Schließlich sagt selbst in striktem CQS nichts, dass ein Befehl keinen Wert zurückgeben kann. Er sagt, er sollte keine externen Berechnungen berechnen oder aufrufen und keine Daten zurückgeben . Entweder C oder Q können sich frei fühlen, ihren Endzustand zurückzugeben, wenn dies für den Anrufer nützlich ist.
Suamere
Die Anzahl der von einem Befehl betroffenen Zeilen vereinfacht jedoch die Erstellung neuer Befehle aus alten Befehlen erheblich. Quelle: jahrelange Erfahrung.
Joshua
@Joshua: Ebenso kann "Neuen Datensatz hinzufügen und eine ID (für einige Strukturen eine Zeilennummer) zurückgeben, die während des Hinzufügens generiert wurde" für Zwecke verwendet werden, die sonst nicht effizient erreicht werden können.
Supercat
Sie sollten die betroffenen Zeilen nicht zurückgeben müssen. Der Befehl sollte wissen, wie viele davon betroffen sind. Wenn es etwas anderes als das # ist, sollte es einen Rollback ausführen und eine Ausnahme auslösen, da gerade etwas Seltsames passiert ist. Das sind also Informationen, die dem Anrufer nicht wirklich wichtig sind (es sei denn, der Benutzer muss das wahre # kennen und Sie können es nicht anhand der angegebenen Daten beurteilen). Es gibt immer noch Grauzonen wie DB-generierte IDs, Stapel / Warteschlangen, in denen sich der Status der Daten ändert, aber ich mag es, in diesen Szenarien eine "CommandQuery" zu erstellen, damit niemand versucht, etwas "Seltsames" zu tun.
Daniel Lorenz
3

Was auch immer der Weg ist, du wirst damit gehen. Es gibt keine Rückgabewerte für die zukünftige Verwendung (z. B. Funktionen geben immer true zurück). Dies ist eine schreckliche Verschwendung von Aufwand und macht den Code nicht klarer lesbar. Wenn Sie einen Rückgabewert haben, sollten Sie ihn immer verwenden, und um ihn verwenden zu können, sollten Sie mindestens 2 mögliche Rückgabewerte haben.

refro
quelle
+1 Dies beantwortet die Frage nicht, ist aber definitiv eine korrekte Information. Bei der Entscheidungsfindung sollte die Entscheidung auf einem Bedürfnis beruhen. Wenn die Anrufer die Rückgabedaten nicht nützlich finden, sollte dies nicht erfolgen. Und niemand kann es gebrauchen, 100% der Zeit für "Future Proofing" zurückzugeben. Zugegeben, wenn "Zukunft" wie ... in ein paar Tagen ist und eine Aufgabe dafür besteht, ist das eine andere Geschichte, lol.
Suamere
1

Ich habe viele leere Funktionen geschrieben. Aber da ich mich mit der ganzen Methode beschäftigt habe, Crack zu verketten, habe ich angefangen, dies zurückzugeben, anstatt es für ungültig zu erklären. Vielleicht kann jemand die Rückgabe ausnutzen, weil man es nicht schaffen kann, mit ungültig zu hocken. Und wenn sie nichts damit anfangen wollen, können sie es einfach ignorieren.

Wyatt Barnett
quelle
1
Die Verkettung von Methoden kann die Vererbung jedoch umständlich und schwierig machen. Ich würde keine Methodenverkettung durchführen, es sei denn, Sie haben einen guten Grund, dies zu tun, als Ihre Methoden nicht "ungültig" zu machen.
KallDrexx
Wahr. Die Vererbung kann jedoch umständlich und schwierig zu handhaben sein.
Wyatt Barnett
Wenn man sich einen unbekannten Code ansieht, der alles zurückgibt, was auch immer (das gesamte Objekt erwähnt) viel Aufmerksamkeit schenkt, prüft man einfach den Fluss innerhalb und außerhalb der Methoden.
Unabhängiger
Ich nehme nicht an, dass Sie Rubyirgendwann hineingekommen sind? Das hat mich in das Verketten von Methoden eingeführt.
KChaloux
Eine Bedenken, die ich bei der Verkettung von Methoden habe, besteht darin, dass es weniger klar macht, ob eine Anweisung wie return Thing.Change1().Change2();erwartet, dass sie sich ändert Thingund einen Verweis auf das Original zurückgibt , oder ob erwartet wird, dass sie einen Verweis auf eine neue Sache zurückgibt, die sich vom Original unterscheidet, während sie das System verlässt original unberührt.
Supercat