Angenommen, ich habe diesen Pseudocode:
bool conditionA = executeStepA();
if (conditionA){
bool conditionB = executeStepB();
if (conditionB){
bool conditionC = executeStepC();
if (conditionC){
...
}
}
}
executeThisFunctionInAnyCase();
Funktionen executeStepX
sollten genau dann ausgeführt werden, wenn die vorherigen erfolgreich sind. In jedem Fall sollte die executeThisFunctionInAnyCase
Funktion am Ende aufgerufen werden. Ich bin ein Neuling in der Programmierung, also entschuldige die sehr grundlegende Frage: Gibt es eine Möglichkeit (zum Beispiel in C / C ++) zu vermeiden, dass diese lange if
Kette diese Art von "Codepyramide" auf Kosten der Lesbarkeit des Codes erzeugt? ?
Ich weiß, wenn wir den executeThisFunctionInAnyCase
Funktionsaufruf überspringen könnten , könnte der Code wie folgt vereinfacht werden:
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
Die Einschränkung ist jedoch der executeThisFunctionInAnyCase
Funktionsaufruf. Könnte die break
Aussage auf irgendeine Weise verwendet werden?
quelle
false
nicht als einer Ausnahmesituation ähnlich angesehen werden?false
Rückkehr kann ziemlich normal sein.Antworten:
Sie können ein
&&
(logisches UND) verwenden:Dies wird Ihre beiden Anforderungen erfüllen:
executeStep<X>()
sollte nur ausgewertet werden, wenn die vorherige erfolgreich war (dies wird als Kurzschlussauswertung bezeichnet )executeThisFunctionInAnyCase()
wird in jedem Fall ausgeführtquelle
&&
und||
), sodass Sie sie auf keinen Fall zu einer einzigenif
Anweisung zusammenfügen möchten, ohne die Lesbarkeit zu beeinträchtigen. Und es ist nicht immer einfach, diese Bedingungen auf äußere Funktionen zu verschieben, da sie möglicherweise von vielen zuvor berechneten lokalen Variablen abhängen, was zu einem schrecklichen Durcheinander führen würde, wenn Sie versuchen, jede einzelne als einzelnes Argument zu übergeben.Verwenden Sie einfach eine zusätzliche Funktion, damit Ihre zweite Version funktioniert:
Wenn Sie entweder tief verschachtelte ifs (Ihre erste Variante) oder den Wunsch verwenden, aus einem "Teil einer Funktion" auszubrechen, benötigen Sie normalerweise eine zusätzliche Funktion.
quelle
foo
arbeitet sich durch eine Abfolge verwandter Bedingungen und Aktionen. Die Funktionbar
ist sauber von den Entscheidungen getrennt. Wenn wir die Details der Bedingungen und Maßnahmen sehen würden, könnte sich herausstellen, dass diesfoo
immer noch zu viel ist, aber im Moment ist dies eine gute Lösung.bar
Ihnen erforderlich sind, müssen Sie sie manuellfoo
als Parameter übergeben. Wenn dies der Fall ist undfoo
nur einmal aufgerufen wird, würde ich mich irren, die goto-Version zu verwenden, um zu vermeiden, dass zwei eng gekoppelte Funktionen definiert werden, die am Ende nicht sehr wiederverwendbar wären.if (!executeStepA() || !executeStepB() || !executeStepC()) return
goto
In diesem Fall verwenden C-Programmierer der alten Schule . Es ist die einzige Verwendunggoto
, die vom Linux-Styleguide tatsächlich empfohlen wird. Sie wird als zentraler Funktions-Exit bezeichnet:Einige Leute arbeiten daran,
goto
indem sie den Körper in eine Schleife wickeln und daraus abbrechen, aber effektiv tun beide Ansätze dasselbe. Dergoto
Ansatz ist besser, wenn Sie nur dann eine andere Bereinigung benötigen, wenn diesexecuteStepA()
erfolgreich war:Mit dem Loop-Ansatz würden Sie in diesem Fall zwei Ebenen von Loops erhalten.
quelle
goto
und denken sofort "Das ist schrecklicher Code", aber er hat seine gültigen Verwendungen.goto
und ich muss nicht einmal denken, um zu sehen, dass dies schrecklicher Code ist. Ich musste einmal so etwas pflegen, es ist sehr böse. Das OP schlägt am Ende der Frage eine vernünftige Alternative in C-Sprache vor, die ich in meine Antwort aufgenommen habe.throw
ist in vielerlei Hinsicht schlimmer alsgoto
weilthrow
es nicht einmal aus dem lokalen Kontext klar ist, wo Sie am Ende landen werden! Verwenden Sie für den Kontrollfluss im goto-Stil dieselben Entwurfsüberlegungen wie für Ausnahmen.Dies ist eine häufige Situation, und es gibt viele gängige Möglichkeiten, damit umzugehen. Hier ist mein Versuch einer kanonischen Antwort. Bitte kommentieren Sie, wenn ich etwas verpasst habe und ich werde diesen Beitrag auf dem neuesten Stand halten.
Dies ist ein Pfeil
Was Sie besprechen, wird als Pfeil-Anti-Muster bezeichnet . Es wird als Pfeil bezeichnet, da die Kette verschachtelter ifs Codeblöcke bildet, die sich immer weiter nach rechts und dann wieder nach links ausdehnen und einen visuellen Pfeil bilden, der auf die rechte Seite des Code-Editor-Bereichs "zeigt".
Flache den Pfeil mit der Wache ab
Einige gebräuchliche Methoden zur Vermeidung des Pfeils werden hier erläutert . Die gängigste Methode ist die Verwendung eines Wachmusters, in dem der Code behandelt die Ausnahme erste fließt und dann übernimmt die Grundströmung, beispielsweise anstelle von
... du würdest benutzen ....
Wenn es eine lange Reihe von Wachen gibt, wird der Code dadurch erheblich abgeflacht, da alle Wachen ganz links angezeigt werden und Ihre Wenns nicht verschachtelt sind. Darüber hinaus koppeln Sie die logische Bedingung visuell mit dem zugehörigen Fehler, wodurch Sie viel einfacher erkennen können, was gerade passiert:
Pfeil:
Bewachen:
Dies ist objektiv und quantifizierbar leichter zu lesen, weil
So fügen Sie am Ende allgemeinen Code hinzu
Das Problem mit dem Schutzmuster besteht darin, dass es auf dem beruht, was als "opportunistische Rückkehr" oder "opportunistischer Ausstieg" bezeichnet wird. Mit anderen Worten, es bricht das Muster, dass jede Funktion genau einen Austrittspunkt haben sollte. Dies ist aus zwei Gründen ein Problem:
Im Folgenden habe ich einige Optionen bereitgestellt, um diese Einschränkung zu umgehen, indem ich entweder Sprachfunktionen verwende oder das Problem insgesamt vermeide.
Option 1. Sie können dies nicht tun: verwenden
finally
Als C ++ - Entwickler können Sie dies leider nicht tun. Dies ist jedoch die Antwort Nummer eins für Sprachen, die ein Schlüsselwort finally enthalten, da dies genau das ist, wofür es ist.
Option 2. Vermeiden Sie das Problem: Restrukturieren Sie Ihre Funktionen
Sie können das Problem vermeiden, indem Sie den Code in zwei Funktionen aufteilen. Diese Lösung hat den Vorteil, dass sie für jede Sprache funktioniert. Darüber hinaus kann sie die zyklomatische Komplexität reduzieren. Dies ist eine bewährte Methode zur Reduzierung Ihrer Fehlerrate und verbessert die Spezifität automatisierter Komponententests.
Hier ist ein Beispiel:
Option 3. Sprachtrick: Verwenden Sie eine gefälschte Schleife
Ein weiterer häufiger Trick, den ich sehe, ist die Verwendung von while (true) und break, wie in den anderen Antworten gezeigt.
Dies ist zwar weniger "ehrlich" als die Verwendung
goto
, aber es ist weniger anfällig, beim Refactoring durcheinander zu kommen, da es die Grenzen des logischen Umfangs klar markiert. Ein naiver Codierer, der Ihre Etiketten odergoto
Aussagen ausschneidet und einfügt, kann große Probleme verursachen! (Und ehrlich gesagt ist das Muster jetzt so verbreitet, dass ich denke, es kommuniziert klar die Absicht und ist daher überhaupt nicht "unehrlich").Es gibt andere Varianten dieser Optionen. Zum Beispiel könnte man
switch
anstelle von verwendenwhile
. Jedes Sprachkonstrukt mit einembreak
Schlüsselwort würde wahrscheinlich funktionieren.Option 4. Nutzen Sie den Objektlebenszyklus
Ein anderer Ansatz nutzt den Objektlebenszyklus. Verwenden Sie ein Kontextobjekt, um Ihre Parameter zu übertragen (etwas, das unserem naiven Beispiel verdächtig fehlt), und entsorgen Sie es, wenn Sie fertig sind.
Hinweis: Stellen Sie sicher, dass Sie den Objektlebenszyklus der Sprache Ihrer Wahl verstehen. Damit dies funktioniert, benötigen Sie eine Art deterministische Garbage Collection, dh Sie müssen wissen, wann der Destruktor aufgerufen wird. In einigen Sprachen müssen Sie
Dispose
anstelle eines Destruktors verwenden.Option 4.1. Nutzen Sie den Objektlebenszyklus (Wrapper-Muster)
Wenn Sie einen objektorientierten Ansatz verwenden möchten, können Sie dies auch richtig machen. Diese Option verwendet eine Klasse, um die zu bereinigenden Ressourcen sowie die anderen Vorgänge zu "verpacken".
Stellen Sie erneut sicher, dass Sie Ihren Objektlebenszyklus verstehen.
Option 5. Sprachtrick: Verwenden Sie die Kurzschlussauswertung
Eine andere Technik besteht darin, die Kurzschlussbewertung zu nutzen .
Diese Lösung nutzt die Arbeitsweise des && Operators. Wenn die linke Seite von && als falsch ausgewertet wird, wird die rechte Seite niemals ausgewertet.
Dieser Trick ist am nützlichsten, wenn kompakter Code erforderlich ist und wenn für den Code wahrscheinlich keine große Wartung erforderlich ist, z. B. wenn Sie einen bekannten Algorithmus implementieren. Für eine allgemeinere Codierung ist die Struktur dieses Codes zu spröde. Selbst eine geringfügige Änderung der Logik kann ein vollständiges Umschreiben auslösen.
quelle
Mach einfach
So einfach ist das.
Aufgrund von drei Änderungen, durch die jede die Frage grundlegend geändert hat (vier, wenn man die Revision auf Version 1 zurückzählt), füge ich das Codebeispiel hinzu, auf das ich antworte:
quelle
&&
Liste viel klarer. Normalerweise unterbreche ich die Bedingungen, um Zeilen zu trennen und// explanation
jeder Zeile ein nachfolgendes hinzuzufügen ... Am Ende ist es massiv weniger Code zu betrachten, und wenn Sie erst einmal verstanden haben, wie es&&
funktioniert, gibt es keine laufenden mentalen Anstrengungen mehr. Mein Eindruck ist, dass die meisten professionellen C ++ - Programmierer damit vertraut sind, aber wie Sie in verschiedenen Branchen / Projekten sagen, unterscheiden sich Fokus und Erfahrung.Es gibt tatsächlich eine Möglichkeit, Aktionen in C ++ zu verschieben: Verwenden des Destruktors eines Objekts.
Angenommen, Sie haben Zugriff auf C ++ 11:
Und dann mit diesem Dienstprogramm:
quelle
executeThisFunctionInAnyCase();
dann ausgeführt wird, wennfoo();
eine Ausnahme ausgelöst wird . Wenn Sie ausnahmesicheren Code schreiben, empfiehlt es sich, alle diese Bereinigungsfunktionen in einem Destruktor abzulegen.foo()
. Und wenn Sie es tun, fangen Sie es. Problem gelöst. Beheben Sie Fehler, indem Sie sie beheben, nicht indem Sie Workarounds schreiben.Defer
Klasse ist ein wiederverwendbarer kleiner Code, mit dem Sie auf ausnahmsichere Weise jede Bereinigung am Ende des Blocks durchführen können. Es ist allgemein bekannt als Scope Guard . Ja, jede Verwendung eines Scope Guard kann auf andere, manuellere Weise ausgedrückt werden, genau wie jedefor
Schleife als Block undwhile
Schleife ausgedrückt werden kann, die wiederum mitif
und ausgedrückt werden können undgoto
die auf Wunsch in Assemblersprache ausgedrückt werden können. oder für diejenigen, die wahre Meister sind, indem sie die Bits im Gedächtnis durch kosmische Strahlen verändern, die durch den Schmetterlingseffekt spezieller kurzer Grunzer und Gesänge gerichtet sind. aber warum das tun?Es gibt eine nette Technik, die keine zusätzliche Wrapper-Funktion mit den return-Anweisungen benötigt (die von Itjax vorgeschriebene Methode). Es wird eine do-
while(0)
Pseudo-Schleife verwendet. Daswhile (0)
stellt sicher, dass es sich tatsächlich nicht um eine Schleife handelt, sondern nur einmal ausgeführt wird. Die Schleifensyntax ermöglicht jedoch die Verwendung der break-Anweisung.quelle
inline
. Auf jeden Fall ist dies eine gute Technik, da sie bei mehr als diesem Problem hilft.Sie können dies auch tun:
Auf diese Weise haben Sie eine minimale lineare Wachstumsgröße von +1 Zeile pro Anruf und können problemlos gewartet werden.
EDIT : (Danke @Unda) Kein großer Fan, weil du die Sichtbarkeit verlierst IMO:
quelle
Würde das funktionieren? Ich denke, das entspricht Ihrem Code.
quelle
ok
wenn ich dieselbe Variable wie diese verwende.Angenommen, der gewünschte Code ist so, wie ich ihn derzeit sehe:
Ich würde sagen, dass der richtige Ansatz, da er am einfachsten zu lesen und am einfachsten zu pflegen ist, weniger Einrückungsstufen aufweist, was (derzeit) der erklärte Zweck der Frage ist.
Dies vermeidet jede Notwendigkeit
goto
s, Ausnahmen, Dummy -while
Schleifen oder andere schwierigen Konstrukte und einfach wird auf mit der einfachen Arbeit an der Hand.quelle
return
und aus ihrbreak
herauszuspringen, ohne zusätzliche "Flag" -Variablen einführen zu müssen. In diesem Fall wäre die Verwendung eines goto ähnlich harmlos - denken Sie daran, dass Sie zusätzliche goto-Komplexität gegen extra veränderbare variable Komplexität eintauschen.newbie
bietet es eine sauberere Lösung ohne Nachteile. Ich stelle fest, dass es auch nicht darauf ankommt,steps
dass die gleiche Signatur vorliegt oder sogar Funktionen statt Blöcke sind. Ich kann sehen, dass dies als Refactor für den ersten Durchgang verwendet wird, selbst wenn ein differenzierterer Ansatz gültig ist.Vielleicht nicht die beste Lösung, aber Sie können Ihre Anweisungen in eine
do .. while (0)
Schleife setzen undbreak
stattdessen Anweisungen verwendenreturn
.quelle
do .. while (0)
für Makrodefinitionen verwendet wird, missbraucht ebenfalls Schleifen, wird jedoch als OK betrachtet.Sie können alle
if
Bedingungen, die nach Ihren Wünschen formatiert sind, in eine eigeneexecuteThisFunctionInAnyCase()
Funktion einfügen und die Funktion bei der Rückgabe ausführen .Aus dem Basisbeispiel im OP können die Bedingungstests und die Ausführung als solche abgespalten werden.
Und dann als solche bezeichnet;
Wenn C ++ 11-Lambdas verfügbar sind (es gab kein C ++ 11-Tag im OP, aber sie sind möglicherweise noch eine Option), können wir auf die separate Funktion verzichten und diese in ein Lambda einpacken.
quelle
Wenn Sie Loops nicht mögen
goto
und nicht mögendo { } while (0);
und C ++ verwenden möchten, können Sie auch ein temporäres Lambda verwenden, um den gleichen Effekt zu erzielen.quelle
if
Sie mögen&&
es nicht, Sie mögen es nicht, {} zu tun, während (0)&&
Sie C ++ mögen ... Entschuldigung, ich konnte nicht widerstehen, aber diese letzte Bedingung schlägt fehl, weil die Frage sowohl mit c als auch mit c ++Die Ketten von IF / ELSE in Ihrem Code sind nicht das Sprachproblem, sondern das Design Ihres Programms. Wenn Sie in der Lage sind, Ihr Programm neu zu faktorisieren oder neu zu schreiben, würde ich vorschlagen, dass Sie in Design Patterns ( http://sourcemaking.com/design_patterns ) nach einer besseren Lösung suchen.
Wenn Sie viele IFs und andere in Ihrem Code sehen, ist dies normalerweise eine Gelegenheit, das Strategie-Design-Muster ( http://sourcemaking.com/design_patterns/strategy/c-sharp-dot-net ) oder eine Kombination zu implementieren von anderen Mustern.
Ich bin mir sicher, dass es Alternativen gibt, um eine lange Liste von if / else zu schreiben, aber ich bezweifle, dass sie irgendetwas ändern werden, außer dass die Kette für Sie hübsch aussieht (jedoch liegt die Schönheit im Auge des Betrachters immer noch für Code auch:-) ) . Sie sollten sich Sorgen machen über Dinge wie (in 6 Monaten, wenn ich einen neuen Zustand habe und mich an nichts über diesen Code erinnere, kann ich ihn einfach hinzufügen? Oder was ist, wenn sich die Kette ändert, wie schnell und fehlerfrei werde ich es umsetzen)
quelle
Du machst das einfach ..
99 mal von 100, das ist der einzige Weg, es zu tun.
Versuchen Sie niemals, etwas "Kniffliges" im Computercode zu tun.
Ich bin mir übrigens ziemlich sicher, dass das Folgende die eigentliche Lösung ist, die Sie sich vorgestellt haben ...
Die continue- Anweisung ist für die algorithmische Programmierung von entscheidender Bedeutung. (Ebenso wie die goto-Anweisung bei der algorithmischen Programmierung von entscheidender Bedeutung ist.)
In vielen Programmiersprachen können Sie dies tun:
(Beachten Sie zunächst: Nackte Blöcke wie dieses Beispiel sind ein kritischer und wichtiger Bestandteil beim Schreiben von schönem Code, insbesondere wenn Sie sich mit "algorithmischer" Programmierung beschäftigen.)
Auch das ist genau das, was Sie in Ihrem Kopf hatten, nicht wahr? Und das ist die schöne Art, es zu schreiben, also hast du gute Instinkte.
Tragischerweise gibt es in der aktuellen Version von Objective-C (abgesehen davon - ich weiß leider nichts über Swift) eine risikoreiche Funktion, bei der überprüft wird, ob der umschließende Block eine Schleife ist.
Hier erfahren Sie, wie Sie das umgehen können ...
Also vergiss das nicht ..
do {} while (false);
bedeutet nur "mache diesen Block einmal".
Das heißt, es gibt absolut keinen Unterschied zwischen Schreiben
do{}while(false);
und einfachem Schreiben{}
.Dies funktioniert jetzt perfekt wie Sie wollten ... hier ist die Ausgabe ...
Es ist also möglich, dass Sie den Algorithmus so in Ihrem Kopf sehen. Sie sollten immer versuchen zu schreiben, was in Ihrem Kopf ist. (Besonders wenn du nicht nüchtern bist, denn dann kommt das Hübsche raus! :))
In "algorithmischen" Projekten, in denen dies häufig vorkommt, haben wir in Ziel-c immer ein Makro wie ...
... also dann kannst du das machen ...
Es gibt zwei Punkte:
a, obwohl es dumm ist, dass objectiv-c die Art des Blocks überprüft, in dem sich eine continue-Anweisung befindet, ist es beunruhigend, "das zu bekämpfen". Es ist also eine schwierige Entscheidung.
b, gibt es die Frage, ob Sie im Beispiel diesen Block einrücken sollten? Ich verliere den Schlaf wegen solcher Fragen, daher kann ich nicht raten.
Ich hoffe es hilft.
quelle
if
, können Sie auch aussagekräftigere Funktionsnamen verwenden und die Kommentare in die Funktionen einfügen.Lassen Sie Ihre Ausführungsfunktionen eine Ausnahme auslösen, wenn sie fehlschlagen, anstatt false zurückzugeben. Dann könnte Ihr Anrufcode folgendermaßen aussehen:
Natürlich gehe ich davon aus, dass in Ihrem ursprünglichen Beispiel der Ausführungsschritt nur dann false zurückgeben würde, wenn innerhalb des Schritts ein Fehler auftritt.
quelle
Viele gute Antworten bereits, aber die meisten scheinen einen Teil (zugegebenermaßen sehr wenig) der Flexibilität zu beeinträchtigen. Ein gängiger Ansatz, der diesen Kompromiss nicht erfordert, ist das Hinzufügen einer Status- / Keep-Going- Variablen. Der Preis ist natürlich ein zusätzlicher Wert, den Sie im Auge behalten sollten:
quelle
ok &= conditionX;
und nicht einfachok = conditionX;
?Wenn Sie in C ++ (die Frage ist sowohl mit C als auch mit C ++ gekennzeichnet) die Funktionen nicht ändern können, um Ausnahmen zu verwenden, können Sie den Ausnahmemechanismus trotzdem verwenden, wenn Sie eine kleine Hilfsfunktion wie schreiben
Dann könnte Ihr Code wie folgt lauten:
Wenn Sie sich für ausgefallene Syntax interessieren, können Sie sie stattdessen über eine explizite Besetzung zum Laufen bringen:
Dann können Sie Ihren Code als schreiben
quelle
Wenn Ihr Code so einfach wie Ihr Beispiel ist und Ihre Sprache Kurzschlussauswertungen unterstützt, können Sie Folgendes versuchen:
Wenn Sie Argumente an Ihre Funktionen übergeben und andere Ergebnisse zurückerhalten, sodass Ihr Code nicht auf die vorherige Weise geschrieben werden kann, sind viele der anderen Antworten besser für das Problem geeignet.
quelle
Für C ++ 11 und höher könnte ein guter Ansatz darin bestehen, ein Scope-Exit- System zu implementieren, das dem Scope-Exit- Mechanismus (Exit- Mechanismus ) von D ähnelt .
Eine Möglichkeit zur Implementierung ist die Verwendung von C ++ 11-Lambdas und einigen Hilfsmakros:
Auf diese Weise können Sie frühzeitig von der Funktion zurückkehren und sicherstellen, dass der von Ihnen definierte Bereinigungscode immer beim Beenden des Bereichs ausgeführt wird:
Die Makros sind wirklich nur Dekoration.
MakeScopeExit()
kann direkt verwendet werden.quelle
[=]
ist normalerweise falsch für ein Lambda mit Zielfernrohr.[&]
: Es ist sicher und minimal überraschend. Erfassung nur nach Wert, wenn das Lambda (oder die Kopien) länger als der Umfang zum Zeitpunkt der Erklärung überleben könnten ...Warum gibt niemand die einfachste Lösung? : D.
Wenn alle Ihre Funktionen dieselbe Signatur haben, können Sie dies folgendermaßen tun (für die Sprache C):
Für eine saubere C ++ - Lösung sollten Sie eine Schnittstellenklasse erstellen, die eine Ausführungsmethode enthält und Ihre Schritte in Objekte einschließt.
Dann sieht die obige Lösung folgendermaßen aus:
quelle
Angenommen, Sie benötigen keine einzelnen Bedingungsvariablen. Wenn Sie die Tests invertieren und das else-falthrough als "ok" -Pfad verwenden, erhalten Sie eine vertikalere Menge von if / else-Anweisungen:
Das Weglassen der fehlgeschlagenen Variablen macht den Code IMO etwas zu dunkel.
Das Deklarieren der Variablen im Inneren ist in Ordnung, keine Sorge um = vs ==.
Das ist dunkel, aber kompakt:
quelle
Dies sieht aus wie eine Zustandsmaschine, was praktisch ist, da Sie sie einfach mit einem Zustandsmuster implementieren können .
In Java würde es ungefähr so aussehen:
Eine Implementierung würde wie folgt funktionieren:
Und so weiter. Dann können Sie die große if-Bedingung durch Folgendes ersetzen:
quelle
Wie Rommik erwähnte, könnten Sie hierfür ein Entwurfsmuster anwenden, aber ich würde das Decorator-Muster anstelle der Strategie verwenden, da Sie Anrufe verketten möchten. Wenn der Code einfach ist, würde ich eine der gut strukturierten Antworten verwenden, um eine Verschachtelung zu verhindern. Wenn es jedoch komplex ist oder eine dynamische Verkettung erfordert, ist das Decorator-Muster eine gute Wahl. Hier ist ein yUML-Klassendiagramm :
Hier ist ein Beispiel für ein LinqPad C # -Programm:
Das beste Buch zum Lesen von Designmustern, IMHO, ist Head First Design Patterns .
quelle
Mehrere Antworten deuteten auf ein Muster hin, das ich oft gesehen und verwendet habe, insbesondere bei der Netzwerkprogrammierung. In Netzwerkstapeln gibt es häufig eine lange Folge von Anforderungen, von denen jede fehlschlagen kann und den Prozess stoppt.
Das übliche Muster war zu verwenden
do { } while (false);
Ich habe ein Makro verwendet
while(false)
, um es zu erstellen.do { } once;
Das übliche Muster war:Dieses Muster war relativ einfach zu lesen und ermöglichte die Verwendung von Objekten, die ordnungsgemäß zerstört und mehrere Rückgaben vermieden wurden, was das Schritt- und Debugging etwas erleichterte.
quelle
Um die C ++ 11-Antwort von Mathieu zu verbessern und die Laufzeitkosten zu vermeiden, die durch die Verwendung von entstehen
std::function
, würde ich empfehlen, Folgendes zu verwendenDiese einfache Vorlagenklasse akzeptiert jeden Funktor, der ohne Parameter aufgerufen werden kann, und zwar ohne dynamische Speicherzuweisungen und entspricht daher besser dem Abstraktionsziel von C ++ ohne unnötigen Overhead. Die zusätzliche Funktionsvorlage soll die Verwendung durch Ableiten von Vorlagenparametern vereinfachen (was für Klassenvorlagenparameter nicht verfügbar ist).
Anwendungsbeispiel:
Genau wie Mathieus Antwort ist diese Lösung völlig ausnahmesicher und
executeThisFunctionInAnyCase
wird in jedem Fall aufgerufen. Sollte sichexecuteThisFunctionInAnyCase
selbst werfen, werden Destruktoren implizit markiertnoexcept
und daher wird ein Aufruf vonstd::terminate
ausgegeben, anstatt zu bewirken, dass beim Abwickeln des Stapels eine Ausnahme ausgelöst wird.quelle
functor
imdeferred
Konstruktor perfekt vorwärts gehen , ohne a erzwingen zu müssenmove
.Anscheinend möchten Sie Ihren gesamten Anruf von einem einzigen Block aus erledigen. Wie andere vorgeschlagen haben, sollten Sie entweder eine
while
Schleife verwenden undbreak
eine neue Funktion verwenden, die Sie verlassen könnenreturn
(möglicherweise sauberer).Ich persönlich verbanne
goto
, auch für den Funktionsausgang. Sie sind beim Debuggen schwerer zu erkennen.Eine elegante Alternative, die für Ihren Workflow funktionieren sollte, besteht darin, ein Funktionsarray zu erstellen und dieses zu iterieren.
quelle
Da Sie zwischen den Ausführungen auch einen [... Codeblock ...] haben , haben Sie vermutlich Speicherzuweisung oder Objektinitialisierungen. Auf diese Weise müssen Sie sich darum kümmern, alles zu bereinigen, was Sie bereits beim Beenden initialisiert haben, und es auch bereinigen, wenn Sie auf ein Problem stoßen und eine der Funktionen false zurückgibt.
In diesem Fall war das Beste, was ich in meiner Erfahrung (als ich mit CryptoAPI arbeitete) hatte, das Erstellen kleiner Klassen. Im Konstruktor initialisieren Sie Ihre Daten, im Destruktor initialisieren Sie sie nicht. Jede nächste Funktionsklasse muss der vorherigen Funktionsklasse untergeordnet sein. Wenn etwas schief gelaufen ist - Ausnahme auslösen.
Und dann müssen Sie in Ihrem Code nur noch anrufen:
Ich denke, es ist die beste Lösung, wenn jeder Aufruf von ConditionX etwas initialisiert, Speicher usw. zuweist. Am besten stellen Sie sicher, dass alles bereinigt wird.
quelle
Ein interessanter Weg ist, mit Ausnahmen zu arbeiten.
Wenn Sie solchen Code schreiben, gehen Sie irgendwie in die falsche Richtung. Ich werde es nicht als "das Problem" ansehen, einen solchen Code zu haben, sondern eine so chaotische "Architektur".
Tipp: Besprechen Sie diese Fälle mit einem erfahrenen Entwickler, dem Sie vertrauen ;-)
quelle
Eine andere
do - while
Ansatzschleife, obwohl sie bereits erwähnt wurde, gab es kein Beispiel dafür, das zeigen würde, wie sie aussieht:(Nun, es gibt bereits eine Antwort mit
while
Schleife, aber diedo - while
Schleife prüft nicht redundant (am Anfang) auf wahr, sondern am Ende xD (dies kann jedoch übersprungen werden)).quelle