Switch-Anweisung mit Rückgabe - Code-Korrektheit


90

Angenommen, ich habe Code in C mit ungefähr dieser Struktur:

switch (something)
{
    case 0:
      return "blah";
      break;

    case 1:
    case 4:
      return "foo";
      break;

    case 2:
    case 3:
      return "bar";
      break;

    default:
      return "foobar";
      break;
}

Natürlich sind die breaks nicht notwendig, damit der Code korrekt ausgeführt wird, aber es sieht nach einer schlechten Praxis aus, wenn ich sie mir nicht dort hinlege.

Was denken Sie? Ist es in Ordnung, sie zu entfernen? Oder würden Sie sie für eine erhöhte "Korrektheit" behalten?

Antworten:


136

Entfernen Sie die breakAnweisungen. Sie werden nicht benötigt und möglicherweise geben einige Compiler Warnungen "Nicht erreichbarer Code" aus .


2
Gleiches gilt für andere bedingungslose Control-Jumping-Anweisungen wie continueoder goto- es ist idiomatisch, sie anstelle von zu verwenden break.
Café

31

Ich würde einen ganz anderen Weg einschlagen. Kehre nicht in der Mitte der Methode / Funktion zurück. Fügen Sie stattdessen einfach den Rückgabewert in eine lokale Variable ein und senden Sie ihn am Ende.

Persönlich finde ich Folgendes besser lesbar:

String result = "";

switch (something) {
case 0:
  result = "blah";
  break;
case 1:
  result = "foo";
  break;
}

return result;

23
Die "One Exit" -Philosophie ist in C sinnvoll, wo Sie sicherstellen müssen, dass die Dinge korrekt aufgeräumt werden. Wenn man bedenkt, dass man in C ++ jederzeit durch eine Ausnahme aus der Funktion herausgerissen werden kann, ist die One-Exit-Philosophie in C ++ nicht wirklich nützlich.
Billy ONeal

29
Theoretisch ist dies eine großartige Idee, erfordert jedoch häufig zusätzliche Steuerblöcke, die die Lesbarkeit beeinträchtigen können.
Mikerobi

8
Der Hauptgrund, warum ich die Dinge so mache, ist, dass es bei längeren Funktionen sehr leicht ist, den Ausgangsvektor beim Durchsuchen von Code zu übersehen. Wenn der Ausgang jedoch immer am Ende ist, ist es einfacher, schnell zu erfassen, was los ist.
NotMe

7
Nun, das und Rückgabeblöcke in der Mitte des Codes erinnern mich nur an GOTO-Missbrauch.
NotMe

5
@ Chris: Bei längeren Funktionen stimme ich voll und ganz zu - aber das spricht normalerweise für größere Probleme, also überarbeiten Sie es. In vielen Fällen ist es eine triviale Funktion, die auf einen Schalter zurückkehrt, und es ist sehr klar, was passieren soll. Verschwenden Sie also keine Gehirnleistung, um eine zusätzliche Variable zu verfolgen.
Stephen

8

Persönlich würde ich die Retouren entfernen und die Pausen behalten. Ich würde die switch-Anweisung verwenden, um einer Variablen einen Wert zuzuweisen. Geben Sie diese Variable dann nach der switch-Anweisung zurück.

Obwohl dies ein strittiger Punkt ist, habe ich immer das Gefühl gehabt, dass gutes Design und Verkapselung einen Weg hinein und einen Weg hinaus bedeuten. Es ist viel einfacher, die Logik zu garantieren, und Sie verpassen nicht versehentlich den Bereinigungscode, der auf der zyklomatischen Komplexität Ihrer Funktion basiert.

Eine Ausnahme: Eine frühzeitige Rückgabe ist in Ordnung, wenn zu Beginn einer Funktion ein fehlerhafter Parameter erkannt wird - bevor Ressourcen erfasst werden.


Könnte für diesen speziellen gut sein switch, aber im Allgemeinen nicht unbedingt am besten. Nehmen wir an, die switch-Anweisung in einer Funktion steht vor weiteren 500 Codezeilen, die nur in bestimmten Fällen gültig sind true. Was bringt es, all diesen zusätzlichen Code für die Fälle auszuführen, in denen er nicht ausgeführt werden muss? ist es nicht besser, nur returnin der switchfür die cases?
Fr0zenFyr

6

Behalten Sie die Pausen bei - es ist weniger wahrscheinlich, dass Sie Probleme bekommen, wenn Sie den Code später bearbeiten, wenn die Pausen bereits vorhanden sind.

Trotzdem wird es von vielen (einschließlich mir) als schlechte Praxis angesehen, aus der Mitte einer Funktion zurückzukehren. Idealerweise sollte eine Funktion einen Eintrittspunkt und einen Austrittspunkt haben.


5

Entferne sie. Es ist idiomatisch, von caseAnweisungen zurückzukehren, und ansonsten ist es "nicht erreichbarer Code".


5

Ich würde sie entfernen. In meinem Buch sollte so ein toter Code als Fehler betrachtet werden, da Sie dadurch zweimal nehmen und sich fragen: "Wie würde ich diese Zeile jemals ausführen?"


4

Normalerweise würde ich den Code ohne sie schreiben. IMO, toter Code deutet auf Schlamperei und / oder Unverständnis hin.

Natürlich würde ich auch etwas in Betracht ziehen wie:

char const *rets[] = {"blah", "foo", "bar"};

return rets[something];

Bearbeiten: Auch mit dem bearbeiteten Beitrag kann diese allgemeine Idee gut funktionieren:

char const *rets[] = { "blah", "foo", "bar", "bar", "foo"};

if ((unsigned)something < 5)
    return rets[something]
return "foobar";

An einem bestimmten Punkt, insbesondere wenn die Eingabewerte dünn sind (z. B. 1, 100, 1000 und 10000), möchten Sie stattdessen ein spärliches Array. Sie können dies entweder als Baum oder als Karte recht gut implementieren (obwohl natürlich auch in diesem Fall ein Switch noch funktioniert).


Der Beitrag wurde bearbeitet, um zu reflektieren, warum diese Lösung nicht gut funktioniert.
Houbysoft

@Ihre Bearbeitung: Ja, aber es würde mehr Speicherplatz benötigen usw. IMHO ist der Schalter der einfachste Weg, was ich in einer so kleinen Funktion will (es macht nur den Schalter).
Houbysoft

3
@houbysoft: Schauen Sie genau hin, bevor Sie zu dem Schluss kommen, dass zusätzlicher Speicher benötigt wird. Bei einem typischen Compiler werden bei zweimaliger Verwendung von "foo" (z. B.) zwei Zeiger auf einen einzelnen Datenblock verwendet und die Daten nicht repliziert. Sie können auch kürzeren Code erwarten. Wenn Sie nicht viele wiederholte Werte haben, wird häufig insgesamt Speicherplatz gespart.
Jerry Coffin

wahr. Ich werde trotzdem an meiner Lösung festhalten, da die Liste der Werte sehr lang ist und ein Schalter einfach schöner erscheint.
Houbysoft

1
In der Tat besteht die elegantere und effizientere Antwort auf ein Problem wie dieses, wenn die Schaltwerte zusammenhängend und der Datentyp homogen sind, darin, ein Array zu verwenden, um einen Wechsel insgesamt zu vermeiden.
Dwayne Robinson

2

Ich würde sagen, entfernen Sie sie und definieren Sie einen Standard: Zweig.


Wenn es keinen sinnvollen Standard gibt, sollten Sie keinen Standard definieren.
Billy ONeal

es gibt eine, und ich habe eine definiert, ich habe sie einfach nicht in die Frage gestellt, sondern jetzt bearbeitet.
Houbysoft

2

Wäre es nicht besser, ein Array mit zu haben?

arr[0] = "blah"
arr[1] = "foo"
arr[2] = "bar"

und tun return arr[something];?

Wenn es um die Praxis im Allgemeinen geht, sollten Sie die breakAnweisungen im Schalter behalten . Für den Fall, dass Sie returnin Zukunft keine Aussagen mehr benötigen , verringert sich die Wahrscheinlichkeit, dass sie zum nächsten fallen case.


Der Beitrag wurde bearbeitet, um zu reflektieren, warum diese Lösung nicht gut funktioniert.
Houbysoft

2

Für "Korrektheit" sind Blöcke mit einem Eingang und einem Ausgang eine gute Idee. Zumindest waren sie es, als ich mein Informatikstudium absolvierte. Also würde ich wahrscheinlich eine Variable deklarieren, sie im Schalter zuweisen und am Ende der Funktion einmal zurückkehren


2

Was denken Sie? Ist es in Ordnung, sie zu entfernen? Oder würden Sie sie für eine erhöhte "Korrektheit" behalten?

Es ist in Ordnung, sie zu entfernen. Die Verwendung return ist genau das Szenario, in dem breaksie nicht verwendet werden sollte.


1

Interessant. Der Konsens aus den meisten dieser Antworten scheint zu sein, dass die redundante breakAussage unnötige Unordnung ist. Andererseits habe ich die breakAussage in einem Schalter als "Abschluss" eines Falls gelesen . caseBlöcke, die nicht in einem enden, breakneigen dazu, auf mich zu springen, wenn potenzielle Fehler auftreten.

Ich weiß, dass es nicht so ist, wenn es ein returnstatt eines gibt break, aber so "lesen" meine Augen die Fallblöcke in einem Schalter, also würde ich es persönlich vorziehen, wenn jeder casemit einem gepaart wird break. Aber viele Compiler beschweren über die breaknach einem returnüberflüssig / nicht erreichbar, und anscheinend scheine ich sowieso in der Minderheit zu sein.

Also loswerden von breaka return.

NB: Bei alledem wird ignoriert, ob ein Verstoß gegen die Ein- / Ausstiegsregel eine gute Idee ist oder nicht. Soweit das geht, habe ich eine Meinung, die sich leider je nach den Umständen ändert ...


0

Ich sage, entferne sie. Wenn Ihr Code so unlesbar ist, dass Sie Pausen einfügen müssen, um auf der sicheren Seite zu sein, sollten Sie Ihren Codierungsstil überdenken :)

Außerdem habe ich es immer vorgezogen, Pausen und Rückgaben in der switch-Anweisung nicht zu mischen, sondern bei einer davon zu bleiben.


0

Ich persönlich neige dazu, die breaks zu verlieren . Möglicherweise liegt eine Ursache für diese Gewohnheit in der Programmierung von Fensterprozeduren für Windows-Apps:

LRESULT WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_SIZE:
            return sizeHandler (...);
        case WM_DESTROY:
            return destroyHandler (...);
        ...
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Ich persönlich finde diesen Ansatz viel einfacher, prägnanter und flexibler, als eine von jedem Handler festgelegte Rückgabevariable zu deklarieren und am Ende zurückzugeben. Angesichts dieses Ansatzes sind die breaks redundant und sollten daher verschwinden - sie dienen keinem nützlichen Zweck (syntaktisch oder visuell IMO) und blähen nur den Code auf.


0

Ich denke, die * Pausen * sind für einen Zweck da. Es soll die "Ideologie" der Programmierung am Leben erhalten. Wenn wir unseren Code nur ohne logische Kohärenz "programmieren" wollen, ist er vielleicht jetzt für Sie lesbar, aber versuchen Sie es morgen. Erklären Sie es Ihrem Chef. Versuchen Sie es unter Windows 3030.

Bleah, die Idee ist sehr einfach:


Switch ( Algorithm )
{

 case 1:
 {
   Call_911;
   Jump;
 }**break**;
 case 2:
 {
   Call Samantha_28;
   Forget;
 }**break**;
 case 3:
 {
   Call it_a_day;
 }**break**;

Return thinkAboutIt?1:return 0;

void Samantha_28(int oBed)
{
   LONG way_from_right;
   SHORT Forget_is_my_job;
   LONG JMP_is_for_assembly;
   LONG assembly_I_work_for_cops;

   BOOL allOfTheAbove;

   int Elligence_says_anyways_thinkAboutIt_**break**_if_we_code_like_this_we_d_be_monkeys;

}
// Sometimes Programming is supposed to convey the meaning and the essence of the task at hand. It is // there to serve a purpose and to keep it alive. While you are not looking, your program is doing  // its thing. Do you trust it?
// This is how you can...
// ----------
// **Break**; Please, take a **Break**;

/ * Nur eine kleine Frage. Wie viel Kaffee haben Sie beim Lesen der obigen Informationen getrunken? IT bricht manchmal das System * /


0

Beenden Sie den Code an einer Stelle. Dies bietet eine bessere Lesbarkeit des Codes. Das Hinzufügen von return-Anweisungen (mehrere Exits) dazwischen erschwert das Debuggen.


0

Wenn Sie einen Code vom Typ "Lookup" haben, können Sie die switch-case-Klausel in eine eigene Methode packen.

Ich habe einige davon in einem "Hobby" -System, das ich zum Spaß entwickle:

private int basePerCapitaIncomeRaw(int tl) {
    switch (tl) {
        case 0:     return 7500;
        case 1:     return 7800;
        case 2:     return 8100;
        case 3:     return 8400;
        case 4:     return 9600;
        case 5:     return 13000;
        case 6:     return 19000;
        case 7:     return 25000;
        case 8:     return 31000;
        case 9:     return 43000;
        case 10:    return 67000;
        case 11:    return 97000;
        default:    return 130000;
    }
}

(Ja. Das ist GURPS Raum ...)

Ich stimme anderen zu, dass Sie in den meisten Fällen mehr als eine Rückgabe in einer Methode vermeiden sollten, und ich erkenne, dass diese möglicherweise besser als Array oder etwas anderes implementiert wurde. Ich fand gerade, dass Switch-Case-Return eine ziemlich einfache Übereinstimmung mit einer Nachschlagetabelle mit einer 1: 1-Korrelation zwischen Eingabe und Ausgabe ist, wie oben beschrieben (Rollenspiele sind voll davon, ich bin sicher, dass sie in anderen existieren auch "Unternehmen"): D.

Wenn andererseits die case-Klausel komplexer ist oder etwas nach der switch-Anweisung passiert, würde ich nicht empfehlen, return darin zu verwenden, sondern eine Variable im switch setzen, mit einer Pause beenden und return der Wert der Variablen am Ende.

(Auf der ... dritten? Hand ... können Sie einen Schalter jederzeit in eine eigene Methode umgestalten ... Ich bezweifle, dass er sich auf die Leistung auswirkt, und es würde mich nicht wundern, wenn moderne Compiler ihn überhaupt erkennen könnten etwas, das eingefügt werden könnte ...)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.