Muss der Standardfall hinzugefügt werden, wenn Schalterfälle verwendet werden?

41

Während einer kürzlich durchgeführten Codeüberprüfung wurde ich gebeten, defaultFälle in alle Dateien zu schreiben switch, wo immer ein Block verwendet wird, auch wenn nichts zu tun ist default. Das heißt, ich muss den defaultFall stellen und nichts darauf schreiben.

Ist das das Richtige? Welchem ​​Zweck würde es dienen?

Ankit
quelle
9
Es liegt an Ihnen ... Ich meine, der Code-Stil des Vorgesetzten.
SD
1
Ich denke, es gibt sogar Fälle, in denen Sie keinen Standardfall möchten. Ziehen Sie in Betracht, eine Aufzählung zu vertauschen. Ihr Schalter definiert einen Fall für alle möglichen Werte. Das Hinzufügen eines Werts zu einer Aufzählung sollte nun zum Hinzufügen eines Falls in dieser Option führen. Wenn Sie dies nicht getan haben, ist dies möglicherweise ein Fehler, da Sie etwas für diesen neuen Aufzählungswert tun möchten , Ihr Code jedoch nicht. Ihr Compiler kann Sie warnen , (glaube ich, nicht sicher Java) darüber, aber nur , wenn Sie haben nicht einen Standardfall setzen. Alternativ können Sie im Standardfall eine Warnung ausgeben, wenn Sie sicher sind, dass der Code sie nie erreicht.
Leemes
6
Ich und meine Freunde nennen dies den "Developer Exception" -Fall. Wenn etwas in dem Code passiert, von dem Sie wissen, dass es niemals passieren sollte (aus logischen / Code-Gründen), fügen wir eine "Developer Exception" hinzu, die ausgelöst wird. Auf diese Weise wird, falls dies jemals passieren sollte, eine Developer Exception ausgelöst, und wir wissen zumindest, wo ernsthafte Fehler aufgetreten sind.
Marco

Antworten:

31

Es scheint drei Fälle zu geben, in denen eine defaultAussage nicht notwendig ist:

  1. Es bleiben keine weiteren Fälle übrig, da nur eine begrenzte Anzahl von Werten in das Feld eingegeben wird switch case. Dies kann sich jedoch mit der Zeit ändern (absichtlich oder versehentlich), und es wäre gut, default casewenn sich etwas ändert. Sie könnten den Benutzer über einen falschen Wert informieren oder ihn warnen.

  2. Sie wissen, wie und wo der switch caseverwendet wird und welche Werte eingegeben werden. Auch dies kann sich ändern und eine zusätzliche Verarbeitung ist möglicherweise erforderlich.

  3. In anderen Fällen ist keine spezielle Verarbeitung erforderlich. Wenn dies der Fall ist, werden Sie gebeten, a hinzuzufügen default case, da dies ein akzeptierter Codierungsstil ist und Ihren Code besser lesbar macht.

Die ersten beiden Fälle beruhen auf Annahmen. Sie können es sich also nicht leisten, diese Annahmen zu treffen (vorausgesetzt, Sie arbeiten in einem nicht so kleinen Team, da Sie regelmäßig Codeprüfungen durchführen). Sie wissen nicht, wer mit Ihrem Code arbeitet oder Funktionen aufruft / Methoden in Ihrem Code aufruft. In ähnlicher Weise müssen Sie möglicherweise mit dem Code einer anderen Person arbeiten. Wenn Sie den gleichen Codierungsstil verwenden, wird es einfacher, mit dem Code einer Person (einschließlich Ihres Codes) umzugehen.

superM
quelle
14
In den Fällen 1 und 2 sollte ein Standardfall ein Fehler sein. Wenn Ihr Codierungsstil immer default verwendet, dann tun Sie dies, aber lassen Sie es einen Fehler in 1 und 2 ausgeben. Ist möglich, sollte es zur Kompilierungszeit ausgegeben werden, aber das ist möglicherweise nicht immer möglich, wenn jemals in Java. Ich denke zumindest, dass es für den Benutzer wichtig ist, die Meldung zu erhalten, dass "Sie sich selbst in den Fuß schießen, indem Sie diesen Wert übergeben"
martiert
11
Ein Standardfall ist immer eine gute Idee, da beispielsweise bei einer Aufzählung, wenn jemand einen neuen Eintrag zur Aufzählung hinzufügt, Ihr Standardfall etwas Ähnliches tun sollte, throw new IllegalStateException("Unrecognised case "+myEnum)das Ihnen zeigt, wo und was der Fehler ist.
Rich
Bedeutet das, dass Sie immer eine elseKlausel haben sollten, if/else ifauch wenn Sie wissen, dass dies nicht passieren sollte? Das scheint mir keine gute Idee zu sein ...
jadkik94
2
Wenn ich eine Anekdote aus meiner Arbeit hinzufügen darf: Ich hatte eine Factory-Methode, die eine Aufzählung verwendete, um einige Klassen zu instanziieren. Ich hatte Anweisungen hinterlassen, dass Sie, wenn Sie diesem Projekt eine neue Klasse hinzufügten, der Aufzählung einen Wert hinzufügen und einen Fall für den Schalter. Natürlich funktioniert der Code eine Woche nach dem Schließen nicht mehr, mit einer der Ausnahmen, die niemals ausgelöst werden dürfen. Ich gehe zum Programmierer und frage ihn: "Haben Sie dem Schalter einen Fall hinzugefügt?" und sicherlich nicht. An diesem Tag habe ich die Ausnahme dahingehend geändert, dass "Wenn Sie diese Meldung erhalten, beschuldigen Sie den letzten Entwickler, der den Code berührt hat".
Ziv
28

Ist das das Richtige? Welchem ​​Zweck würde es dienen?

Es ist nicht ungewöhnlich, dass Kodierungsstandards von Unternehmen für alle switchAnweisungen einen Standardfall vorschreiben . Ein Grund dafür ist, dass es den Lesern leicht gemacht wird, das Ende des Internet zu finden switch. Ein weiterer, wahrscheinlich besserer Grund ist, dass Sie eine Sekunde lang darüber nachdenken, was Ihr Code tun soll, wenn die Bedingung nicht Ihren Erwartungen entspricht. Unabhängig vom Grund für die Anforderung sollten Sie, wenn es sich um einen Unternehmensstandard handelt, diesen befolgen, es sei denn, es gibt einen luftdichten Grund, dies nicht zu tun.

Wenn Sie glauben, dass Sie switchFälle für jede mögliche Bedingung einschließen, ist es eine gute Sache, eine assertAussage in den Standardfall zu setzen . Wenn jemand den Code ändert und versehentlich eine Bedingung hinzufügt, die von Ihnen switchnicht abgedeckt wird, trifft er die assertund merkt, dass er diesen Teil des Codes adressieren muss.

Wenn Sie switchnur einige der möglichen Bedingungen abdecken, für die anderen jedoch keine besonderen Maßnahmen erforderlich sind, können Sie den Standardfall leer lassen. Es ist eine gute Idee, in diesem Fall einen Kommentar hinzuzufügen, um anzuzeigen, dass der Standardfall absichtlich leer ist, da die Bedingungen, unter denen er auftritt, keine Arbeit erfordern.

Caleb
quelle
1
was ein assert?
FLY
1
@FLY Es ist ein Schlüsselwort in Java und normalerweise ein Makro in C, C ++ usw. In beiden Fällen wird eine Zusicherung verwendet, um zu testen, ob eine Annahme wahr bleibt und eine Ausnahme auslöst oder andernfalls einen Fehler verursacht, wenn dies nicht der Fall ist.
Caleb
Ich habe meinen vorherigen Kommentar mit einem Link zur Wikipedia bearbeitet
FLY
Sie sollten keinen leeren Fall lassen. Wenn Ihr Switch nicht alle möglichen Bedingungen abdeckt, sollten Sie die anderen Bedingungen in Ihrem Standardfall aus demselben Grund geltend machen, aus dem Sie in Ihrem zweiten Absatz vorgeschlagen haben, eine Zusicherung in den Standardfall einzufügen.
Rold2007
12

Wenn Sie den reinen Aufzählungstyp "einschalten", ist ein Standard-Fallback gefährlich. Wenn Sie später Werte zum Aufzählungstyp hinzufügen, hebt der Compiler Schalter mit neuen Werten hervor, die nicht behandelt werden. Wenn Sie dort eine Standardklausel haben, bleibt der Compiler stumm und Sie vermissen sie möglicherweise.

Artur
quelle
1
+1 für das Erkennen des Problems zur Kompilierungszeit und nicht zur Laufzeit.
SylvainD
Ich frage mich, wie eine völlig irreführende Antwort gewählt und akzeptiert wird. Schnelles googeln zeigt, dass Java Compiler eine solche Warnung hat. Warum um alles in der Welt werden Sie zur Laufzeit auf Fehler warten, Leute?
Artur
Ich wusste nichts über dieses Verhalten. Mein ideone Codebeispiel ideone.com/WvytWq schlägt etwas anderes vor. Angenommen, Sie sagen, es ist wahr, was passiert, wenn jemand einem Aufzählungstyp Werte hinzufügt, Sie aber Ihren Code nicht neu kompilieren?
Emory
1
Offensichtlich werden neue Werte von switch-Anweisungen nur in Groß- und Kleinschreibung behandelt. Wenn Sie Ihre Implementierungen jedoch nicht neu kompilieren, wenn sich die Schnittstellen ändern, ist Ihr Build-System wirklich kaputt.
Artur
9

In vielerlei Hinsicht ist diese Frage die gleiche wie die oft gefragt Benötige ich eine elseKlausel am Ende ein if/ else ifLeiter , dass Abdeckungen jede Option .

Die Antwort lautet syntaktisch: Nein, das tust du nicht. Aber es gibt doch einen ...

Eine defaultKlausel kann aus (mindestens) zwei Gründen vorhanden sein:

  1. Als Fehlerbehandler - sicher, sollte ich nie hierher kommen! ist ein Anspruch, der geltend gemacht werden kann, aber was ist, wenn dies der Fall ist? Datenverfälschung oder, noch schlimmer, keine Datenüberprüfung führt zu Programmfehlern, wenn diese nicht ordnungsgemäß abgefangen werden. In diesem Fall sollte es keine leere Klausel sein!
  2. Design / Code Coverage - Selbst in der einfachsten Form eines Flussdiagramms gibt es zwei Routen von einer if-Anweisung und immer eine otherwisevon einem Fall. Es gibt überhaupt keinen Grund, diese nicht in den Quellcode aufzunehmen.

Meine Philosophie ist immer ganz einfach: Schätzen Sie das Worst-Case-Szenario der beiden Optionen ein und wählen Sie die sicherste. Im Falle eines Leerzeichens elseoder einer defaultKlausel sind die Worst-Case-Optionen:

  • Fügen Sie sie hinzu: Drei oder vier zusätzliche Zeilen "redundanten" Codes.
  • Nicht einschließen: Rogue-Daten oder ein unerwarteter Zustand werden nicht abgefangen, was möglicherweise zu einem Programmfehler führt.

Überdramatisch? Vielleicht ... aber dann hat meine Software das Potenzial, Leute zu töten, wenn es schief geht. Ich gehe dieses Risiko lieber nicht ein.


Im Übrigen empfehlen die MISRA-C-Richtlinien (siehe Profil für die Zugehörigkeit) defaultfür jeden eine Klauselswitch

Andrew
quelle
Nicht nur am Ende einer Leiter. Die Frage ist: Muss ich eine elsenach ein if. Aber wie Sie sagten, nein, das ist es nicht.
ott--
Als Fehlerbehandlungsroutine schlage ich ein leicht modifiziertes Formular vor, das den leeren Standard vermeidet. Was das Töten von Menschen angeht, denke ich nicht, dass es wirklich wichtig ist - die Menschen werden genauso tot sein, unabhängig davon, ob Sie die Standardklausel haben oder nicht, aber es wird einfacher, herauszufinden, warum sie gestorben sind.
Emory
Guter Punkt, @emory - das war nicht sehr klar, war es ... bearbeitet :)
Andrew
6

Java zwingt Sie nicht dazu, eine 'Standard'-Anweisung zu haben, aber es ist eine gute Praxis , immer eine zu haben, selbst wenn der Code (im Moment) möglicherweise nie erreicht wird. Hier sind einige Gründe:

Durch eine nicht erreichbare Standardklausel zeigen Sie dem Leser Ihres Codes, dass Sie die Werte berücksichtigt haben und wissen, was Sie tun. Sie können auch zukünftige Änderungen berücksichtigen, zum Beispiel: Es wird ein neuer Enum-Wert hinzugefügt. Der Switch sollte den neuen Wert nicht unbemerkt ignorieren. Sie können dort stattdessen eine Ausnahme auslösen oder etwas anderes tun.

Um einen unerwarteten Wert abzufangen (falls Sie keine Aufzählung aktivieren), der übergeben wird, kann dieser beispielsweise größer oder kleiner als erwartet sein.

'Standard'-Aktionen handhaben - wobei die Schalter für ein spezielles Verhalten sind. Zum Beispiel kann eine Variable außerhalb des Schalters deklariert, aber nicht initialisiert werden, und in jedem Fall wird sie mit etwas anderem initialisiert. Standard in diesem Fall könnte es auf einen Standardwert initialisieren, damit der Code unmittelbar nach dem Wechsel keine Ausnahme auslöst.

Selbst wenn Sie beschließen, nichts in die Standardeinstellung aufzunehmen (keine Ausnahmen, Protokollierung usw.), kann sogar ein Kommentar, der besagt, dass die Standardeinstellung niemals auftreten wird, die Lesbarkeit Ihres Codes verbessern. aber das hängt von persönlichen Vorlieben ab.

Deko
quelle
2

Ich möchte auch hinzufügen, dass dies von der Philosophie der von Ihnen verwendeten Sprache abhängt. In Erlang zum Beispiel, wo es ein gängiger Ansatz ist, "es zum Absturz zu bringen", würden Sie keinen Standardfall definieren, sondern Ihre caseAnweisung einen Fehler auslösen lassen, wenn eine Situation eintritt, die nicht berücksichtigt wurde.

Rodrigue
quelle
0

Sie sollten immer eine Zahlungsunfähigkeit haben, es sei denn, Sie haben nachweislich und dauerhaft alle Fälle abgedeckt. Es kostet nichts und ist eine Versicherung gegen das Versagen, Ihre switch-Anweisung in einem sich entwickelnden Programm zu pflegen.

Wenn Sie wirklich sicher sind, dass Sie sich nicht um andere Fälle kümmern, ist der Standardwert: break; // egal, aber der Standardwert sollte ungefähr so ​​lauten wie der Standardwert: throw new Error ("unerwarteter Wert");

Ebenso sollten Sie niemals ein nicht triviales Fallkonstrukt "durchgehen", ohne einen Kommentar zu dem Effekt hinzuzufügen, den Sie durchgehen möchten. Java hat dies in der Entwurfsphase falsch verstanden.

ddyer
quelle
-2

Erwägen

enum Gender
{
       MALE ,
       FEMALE ;
}

und

void run ( Gender g )
{
      switch ( g )
      {
           case MALE :
                 // code related to males
                 break ;
           default :
                 assert Gender . FEMALE . equals ( g ) ;
                 // code related to females
                 break ;
      }
}

Ich würde argumentieren, dass dies besser ist als ein MÄNNLICHER Fall, ein WEIBLICHER Fall und ein Standardfall, der eine Ausnahme auslöst.

Während der Testphase prüft der Computer, ob g WEIBLICH ist. Während der Produktion entfällt die Prüfung. (Ja, eine Mikrooptimierung!)

Noch wichtiger ist, dass Sie Ihr Ziel einer 100% igen Codeabdeckung beibehalten. Wenn Sie einen Standardfall geschrieben haben, der eine Ausnahme auslöst, wie würden Sie ihn jemals testen?

Emory
quelle
1
1. Keine Notwendigkeit für Mikrooptimierung - Dead Code Elimination wird in Compilern seit etwa 30 Jahren verwendet und von JIT beseitigt. 2. Eine 100% ige Codeabdeckung wird normalerweise nicht als wichtig angesehen (auf der einen Seite erfasst eine 100% ige Abdeckung möglicherweise nicht alle Randfälle, auf der anderen Seite erfasst kein Test assert_not_reached-Zeilen im richtigen Programm). In diesem Fall würde das Verlassen eines Standardfalls einen Compilerfehler verursachen. Auf der anderen Seite - wie würden Sie prüfen, ob die Behauptung funktioniert (Sie verschieben das Problem und verbergen es zu 100%, anstatt die gleiche Zeile zu haben: "Es wird nicht passieren, das verspreche ich")?
Maciej Piechotka
1
3. Wann wird Gender . FEMALE . equals ( FEMALE ) ;ausgewertet false? Sie meinten, Gender.FEMALE.equals (g)aber da Sie sich darauf verlassen können, dass Verweise auf Listen stabil sind, können Sie einfach schreiben g == Gender.FEMALE. 4. (Persönliche Meinung) Dieser Code ist viel schwerer zu lesen und führt zu etwas verwirrenderen Fehlern.
Maciej Piechotka
@MaciejPiechotka guter Fang über g. Ja, die Mikrooptimierung ist kein Vorteil, aber auch kein Nachteil. Eine Codeabdeckung von 100% ist ein Vorteil, wenn dies eines Ihrer Ziele ist und Ihr Codeabdeckungstool keine Asserts überprüft (was nicht der Fall sein sollte).
Emory
Warum sollte es dann eines meiner Ziele sein? Das "interessante" Teil sollte für alle Kantenfälle getestet werden (unabhängig davon, ob sie Codezeilen zugeordnet sind), und "langweilige" Teile können einfach übersprungen werden, um Zeit beim Testen zu sparen. IMHO Code Coverage ist eine schlechte Metrik der Tests.
Maciej Piechotka