it-swarm.dev

Elegante Möglichkeiten, mit if (falls sonst) else umzugehen

Dies ist ein kleines Problem, aber jedes Mal, wenn ich so etwas codieren muss, stört mich die Wiederholung, aber ich bin mir nicht sicher, ob eine der Lösungen nicht schlechter ist.

if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
    }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}
  • Gibt es einen Namen für diese Art von Logik?
  • Bin ich ein bisschen zu OCD?

Ich bin offen für böse Code-Vorschläge, schon aus Neugier ...

165
Benjol

Extrahieren Sie es in eine separate Funktion (Methode) und verwenden Sie die Anweisung return:

if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
        return;
    }
}

DefaultAction();

Oder, vielleicht besser, getrenntes Abrufen von Inhalten und deren Verarbeitung:

contents_t get_contents(name_t file)
{
    if(!FileExists(file))
        return null;

    contents = OpenFile(file);
    if(!SomeTest(contents)) // like IsContentsValid
        return null;

    return contents;
}

...

contents = get_contents(file)
contents ? DoSomething(contents) : DefaultAction();

Upd:

Warum nicht Ausnahmen, warum OpenFile nicht IO Ausnahme:
Ich denke, dass es sich eher um eine generische Frage als um eine Frage zur Datei-E/A handelt. Namen wie FileExists, OpenFile können verwirrend sein, aber wenn sie durch Foo, Bar usw. ersetzt werden sollen, ist es klarer, dass DefaultAction kann so oft aufgerufen werden wie DoSomething, daher kann dies kein Ausnahmefall sein. Péter Török schrieb darüber am Ende seiner Antwort

Warum es in der 2. Variante einen ternären bedingten Operator gibt:
Wenn es ein [C++] -Tag geben würde, hätte ich die Anweisung if mit der Deklaration contents in ihrem Bedingungsteil geschrieben:

if(contents_t contents = get_contents(file))
    DoSomething(contents);
else
    DefaultAction();

Für andere (C-ähnliche) Sprachen ist if(contents) ...; else ...; genau das gleiche wie die Ausdrucksanweisung mit ternärem Bedingungsoperator, jedoch länger. Da der Hauptteil des Codes die Funktion get_contents War, habe ich nur die kürzere Version verwendet (und auch den Typ contents weggelassen). Wie auch immer, es steht außer Frage.

97
Abyx

Wenn die von Ihnen verwendete Programmiersprache (0) binäre Vergleiche kurzschließt (dh wenn SomeTest nicht aufgerufen wird, wenn FileExists false zurückgibt) und (1) die Zuweisung einen Wert zurückgibt (das Ergebnis von OpenFile wird contents zugewiesen, und dieser Wert wird dann als Argument an SomeTest) übergeben. Sie können Folgendes verwenden, dies wird Ihnen jedoch weiterhin empfohlen Kommentieren Sie den Code und stellen Sie fest, dass das einzelne = ist beabsichtigt.

if( FileExists(file) && SomeTest(contents = OpenFile(file)) )
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}

Abhängig davon, wie kompliziert das if ist, ist es möglicherweise besser, eine Flag-Variable zu haben (die das Testen von Erfolgs-/Fehlerbedingungen mit dem Code trennt, der den Fehler DefaultAction in diesem Fall behandelt).

56
frozenkoi

Ernsthafter als die Wiederholung des Aufrufs von DefaultAction ist der Stil selbst, da der Code nicht orthogonal geschrieben ist (siehe diese Antwort aus guten Gründen für das orthogonale Schreiben).

Um zu zeigen, warum nicht orthogonaler Code schlecht ist, betrachten Sie das ursprüngliche Beispiel, wenn eine neue Anforderung eingeführt wird, dass die Datei nicht geöffnet werden soll, wenn sie auf einer Netzwerkfestplatte gespeichert ist. Nun, dann könnten wir den Code einfach auf Folgendes aktualisieren:

if(FileExists(file))
{
    if(! OnNetworkDisk(file))
    {
        contents = OpenFile(file); // <-- prevents inclusion in if
        if(SomeTest(contents))
        {
            DoSomething(contents);
        }
        else
        {
            DefaultAction();
        }
    }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}

Aber dann kommt auch die Anforderung, dass wir auch keine großen Dateien über 2 GB öffnen sollten. Nun, wir aktualisieren einfach noch einmal:

if(FileExists(file))
{
    if(LessThan2Gb(file))
    {
        if(! OnNetworkDisk(file))
        {
            contents = OpenFile(file); // <-- prevents inclusion in if
            if(SomeTest(contents))
            {
                DoSomething(contents);
            }
            else
            {
                DefaultAction();
            }
        }
        else
        {
            DefaultAction();
        }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}

Es sollte sehr klar sein, dass ein solcher Codestil ein großer Wartungsschmerz sein wird.

Zu den Antworten, die hier richtig orthogonal geschrieben sind, gehören Abyx 'zweites Beispiel und Jan Hudecs Antwort , daher werde ich das nicht wiederholen, sondern nur darauf hinweisen, dass die beiden Anforderungen in diesen hinzugefügt werden Antworten wären einfach

if(! LessThan2Gb(file))
    return null;

if(OnNetworkDisk(file))
    return null;

(oder goto notexists; Anstatt von return null;), beeinflusst keinen anderen Code als die hinzugefügten Zeilen . Z.B. senkrecht.

Beim Testen sollte die allgemeine Regel lauten: Testausnahmen, nicht der Normalfall .

26
hlovdal

Offensichtlich:

Whatever(Arguments)
{
    if(!FileExists(file))
        goto notexists;
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(!SomeTest(contents))
        goto notexists;
    DoSomething(contents);
    return;
notexists:
    DefaultAction();
}

Du hast gesagt, du bist sogar offen für böse Lösungen, also zählt es, böse Goto-Zählungen zu verwenden, nein?

In der Tat kann diese Lösung je nach Kontext weniger böse sein als entweder das Böse, das die Aktion zweimal ausführt, oder das Böse als zusätzliche Variable. Ich habe es in eine Funktion eingewickelt, weil es in der Mitte der langen Funktion definitiv nicht in Ordnung wäre (nicht zuletzt aufgrund der Rückkehr in der Mitte). Aber als lange Funktion ist nicht OK, Punkt.

Wenn Sie Ausnahmen haben, sind diese leichter zu lesen, insbesondere wenn OpenFile und DoSomething einfach eine Ausnahme auslösen können, wenn die Bedingungen nicht erfüllt sind, sodass Sie überhaupt keine expliziten Überprüfungen benötigen. Auf der anderen Seite ist in C++ Java und C #, die eine Ausnahme auslösen, eine langsame Operation, daher ist das Goto aus Sicht der Leistung immer noch vorzuziehen.


Anmerkung zu "böse": C++ FAQ 6.15 definiert "böse" als:

Es bedeutet, dass so und so etwas ist, das Sie vermeiden sollten am meisten der Zeit, aber nicht etwas, das Sie vermeiden sollten all the Zeit. Zum Beispiel werden Sie diese "bösen" Dinge immer dann verwenden, wenn sie "die am wenigsten bösen der bösen Alternativen" sind.

Und das gilt in diesem Zusammenhang für goto. Strukturierte Flusskontrollkonstrukte sind die meiste Zeit besser, aber wenn Sie in eine Situation geraten, in der sie zu viele ihrer eigenen Übel ansammeln, wie z. B. Zuweisung in Zustand, Verschachtelung von mehr als 3 Ebenen Tiefe, Duplizieren von Code oder langen Bedingungen, goto kann einfach weniger böse sein.

25
Jan Hudec

Eine Möglichkeit:

boolean handled = false;

if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
        handled = true;
    }
}
if (!handled)
{
    DefaultAction();
}

Dies macht den Code natürlich auf andere Weise etwas komplexer. Es ist also größtenteils eine Stilfrage.

Ein anderer Ansatz wäre die Verwendung von Ausnahmen, z.

try
{
    contents = OpenFile(file); // throws IO exception if file not found
    DoSomething(contents); // calls SomeTest() and throws exception on failure
}
catch(Exception e)
{
    DefaultAction();
    // and the exception should be at least logged...
}

Dies sieht einfacher aus, ist jedoch nur anwendbar, wenn

  • wir wissen genau, welche Art von Ausnahmen zu erwarten sind, und DefaultAction() passt zu jeder
  • wir erwarten, dass die Dateiverarbeitung erfolgreich ist, und eine fehlende Datei oder ein fehlerhaftes SomeTest() ist eindeutig eine fehlerhafte Bedingung, daher ist es geeignet, eine Ausnahme darauf auszulösen.
12
Péter Török
function FileContentsExists(file) {
    return FileExists(file) ? OpenFile(file) : null;
}

...

contents = FileContentExists(file);
if(contents && SomeTest(contents))
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}
12
herby

Funktionen sollten eines tun. Sie sollten es gut machen. Sie sollten es nur tun.
- Robert Martin in Clean Code

Einige Leute finden diesen Ansatz etwas extrem, aber er ist auch sehr sauber. Lassen Sie mich in Python veranschaulichen:

def processFile(self):
    if self.fileMeetsTest():
        self.doSomething()
    else:
        self.defaultAction()

def fileMeetsTest(self):
    return os.path.exists(self.path) and self.contentsTest()

def contentsTest(self):
    with open(self.path) as file:
        line = file.readline()
        return self.firstLineTest(line)

Wenn er sagt, Funktionen sollten eine Sache tun, bedeutet er eine Sache. processFile() wählt eine Aktion basierend auf dem Ergebnis eines Tests aus, und das ist alles, was es tut. fileMeetsTest() kombiniert alle Bedingungen des Tests und das ist alles, was es tut. contentsTest() überträgt die erste Zeile an firstLineTest() und das ist alles, was es tut.

Es scheint eine Menge Funktionen zu haben, aber es liest sich praktisch wie reines Englisch:

Überprüfen Sie zum Verarbeiten der Datei, ob sie den Test erfüllt. Wenn ja, dann tun Sie etwas. Andernfalls führen Sie die Standardaktion aus. Die Datei erfüllt den Test, falls vorhanden, und besteht den Inhaltstest. Öffnen Sie zum Testen des Inhalts die Datei und testen Sie die erste Zeile. Der Test für die erste Zeile ...

Zugegeben, das ist ein wenig wortreich, aber beachten Sie, dass ein Betreuer, der sich nicht um die Details kümmert, nach nur den 4 Codezeilen in processFile() aufhören kann zu lesen, und dennoch über gute Kenntnisse auf hohem Niveau verfügt von dem, was die Funktion tut.

11
Karl Bielefeldt

Dies ist auf einer höheren Abstraktionsebene:

if (WeCanDoSomething(file))
{
   DoSomething(contents);
}
else
{
   DefaultAction();
} 

Und das füllt die Details aus.

boolean WeCanDoSomething(file)
{
    if FileExists(file)
    {
        contents = OpenFile(file);
        return (SomeTest(contents));
    }
    else
    {
        return FALSE;
    }
}
11
Jeanne Pindar

In Bezug auf das, was dies genannt wird, kann es sich leicht zum Pfeilspitzen-Anti-Muster entwickeln, wenn Ihr Code wächst, um mehr Anforderungen zu erfüllen (wie aus der bereitgestellten Antwort hervorgeht at https://softwareengineering.stackexchange.com/a/122625/33922 ) und gerät dann in die Falle, große Codeabschnitte mit verschachtelten bedingten Anweisungen zu haben, die einem Pfeil ähneln.

Siehe Links wie;

http://codinghorror.com/blog/2006/01/flattening-arrow-code.html

http://lostechies.com/chrismissal/2009/05/27/anti-patterns-and-worst-practices-the-arrowhead-anti-pattern/

Es gibt noch viel mehr zu diesem und anderen Anti-Mustern bei Google.

Einige großartige Tipps, die Jeff in seinem Blog dazu gibt, sind:

1) Ersetzen Sie die Bedingungen durch Schutzklauseln.

2) Zerlegen Sie bedingte Blöcke in separate Funktionen.

3) Konvertieren Sie negative Schecks in positive Schecks

4) Kehren Sie immer opportunistisch so schnell wie möglich von der Funktion zurück.

Lesen Sie auch einige Kommentare in Jeffs Blog zu Steve McConnells Vorschlägen zu vorzeitigen Rückgaben.

"Verwenden Sie eine Rückgabe, wenn sie die Lesbarkeit verbessert: Wenn Sie in bestimmten Routinen die Antwort kennen, möchten Sie sie sofort an die aufrufende Routine zurückgeben. Wenn die Routine so definiert ist, dass keine weitere Bereinigung erforderlich ist Wenn ein Fehler erkannt wird und nicht sofort zurückgegeben wird, müssen Sie mehr Code schreiben. "

...

"Minimieren Sie die Anzahl der Rückgaben in jeder Routine: Es ist schwieriger, eine Routine zu verstehen, wenn Sie beim Lesen unten nicht wissen, dass sie irgendwo oben zurückgegeben wurde. Verwenden Sie die Rückgaben daher mit Bedacht - nur dann, wenn sie sich verbessern." Lesbarkeit. "

Ich habe immer die 1 Ein-/Ausstiegstheorie pro Funktionstheorie abonniert, aufgrund dessen, was mir vor ungefähr 15 Jahren beigebracht wurde. Ich denke, dies macht den Code so viel einfacher zu lesen und, wie Sie bereits erwähnt haben, wartbarer

6
Mr Moose

Dies entspricht den DRY-, No-Goto- und No-Multiple-Return-Regeln, ist meiner Meinung nach skalierbar und lesbar:

success = FileExists(file);
if (success)
{
    contents = OpenFile(file);
    success = SomeTest(contents);
}
if (success)
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}
6
mouviciel

Für diesen speziellen Fall ist die Antwort einfach genug ...

Es gibt eine Race-Bedingung zwischen FileExists und OpenFile: Was passiert, wenn die Datei entfernt wird?

Der einzig vernünftige Weg, um mit diesem speziellen Fall umzugehen, besteht darin, FileExists zu überspringen:

contents = OpenFile(file);
if (!contents) // open failed
    DefaultAction();
else (SomeTest(contents))
    DoSomething(contents);

Dies löst dieses Problem ordentlich nd macht den Code sauberer.

Im Allgemeinen: Versuchen Sie, das Problem zu überdenken und eine andere Lösung zu finden, die das Problem vollständig vermeidet.

3
Martin Wickman

Ich würde es in eine separate Methode extrahieren und dann:

if(!FileExists(file))
{
    DefaultAction();
    return;
}

contents = OpenFile(file);
if(!SomeTest(contents))
{
    DefaultAction();
    return;
}

DoSomething(contents);

was auch erlaubt

if(!FileExists(file))
{
    DefaultAction();
    return Result.FileNotFound;
}

contents = OpenFile(file);
if(!SomeTest(contents))
{
    DefaultAction();
    return Result.TestFailed;
}

DoSomething(contents);
return Result.Success;            

dann könnten Sie möglicherweise die DefaultAction -Aufrufe entfernen und die Ausführung des DefaultAction für den Aufrufer verlassen:

Result OurMethod(file)
{
    if(!FileExists(file))
    {
        return Result.FileNotFound;
    }

    contents = OpenFile(file);
    if(!SomeTest(contents))
    {
        return Result.TestFailed;
    }

    DoSomething(contents);
    return Result.Success;            
}

void Caller()
{
    // something, something...

    var result = OurMethod(file);
    // if (result == Result.FileNotFound || result == Result.TestFailed), or just
    if (result != Result.Success)        
    {
        DefaultAction();
    }
}

Ich mag Jeanne Pindars Ansatz auch.

3
Konrad Morawski

Eine andere Möglichkeit, wenn Sie nicht zu viele andere sehen möchten, besteht darin, die Verwendung von else ganz zu streichen und eine zusätzliche return-Anweisung einzugeben. Else ist überflüssig, es sei denn, Sie benötigen eine komplexere Logik, um festzustellen, ob es mehr als nur zwei Aktionsmöglichkeiten gibt.

So könnte Ihr Beispiel werden:

void DoABunchOfStuff()
{
    if(FileExists(file))
    {
        DoSomethingWithFileContent(file);
        return;
    }

    DefaultAction();
}

void DoSomethingWithFileContent(file)
{        
    var contents = GetFileContents(file)

    if(SomeTest(contents))
    {
        DoSomething(contents);
        return;
    }

    DefaultAction();
}

AReturnType GetFileContents(file)
{
    return OpenFile(file);
}

Persönlich macht es mir nichts aus, die else -Klausel zu verwenden, da sie explizit angibt, wie die Logik funktionieren soll, und so die Lesbarkeit Ihres Codes verbessert. Einige Tools zur Codeverschönerung ziehen es jedoch vor, auf eine einzelne if -Anweisung zu vereinfachen, um die Verschachtelungslogik zu entmutigen.

2
S.Robins

Der im Beispielcode gezeigte Fall kann normalerweise auf eine einzelne if -Anweisung reduziert werden. Auf vielen Systemen gibt die Funktion zum Öffnen von Dateien einen ungültigen Wert zurück, wenn die Datei noch nicht vorhanden ist. Manchmal ist dies das Standardverhalten. In anderen Fällen muss es über ein Argument angegeben werden. Dies bedeutet, dass der FileExists -Test gelöscht werden kann, was auch bei Race-Bedingungen hilfreich sein kann, die sich aus dem Löschen von Dateien zwischen dem Existenz-Test und dem Öffnen von Dateien ergeben.

file = OpenFile(path);
if(isValidFileHandle(file) && SomeTest(file)) {
    DoSomething(file);
} else {
    DefaultAction();
}

Dies behebt das Problem des Mischens auf Abstraktionsebene nicht direkt, da es das Problem mehrerer nicht verkettbarer Tests vollständig umgeht, obwohl die Abschaffung des Dateiexistenztests nicht mit der Trennung von Abstraktionsebenen unvereinbar ist. Angenommen, ungültige Dateihandles entsprechen "false" und Dateihandles werden geschlossen, wenn sie den Gültigkeitsbereich verlassen:

OpenFileIfSomething(path:String) : FileHandle {
    file = OpenFile(path);
    if (file && SomeTest(file)) {
        return file;
    }
    return null;
}

...

if ((file = OpenFileIfSomething(path))) {
    DoSomething(file);
} else {
    DefaultAction();
}
2
outis

Ich bin mit Frozenkoi einverstanden, aber für C # dachte ich trotzdem, es würde helfen, die Syntax der TryParse-Methoden zu befolgen.

if(FileExists(file) && TryOpenFile(file, out contents))
    DoSomething(contents);
else
    DefaultAction();
bool TryOpenFile(object file, out object contents)
{
    try{
        contents = OpenFile(file);
    }
    catch{
        //something bad happened, computer probably exploded
        return false;
    }
    return true;
}
2
Biff MaGriff

Was ist los mit dem Offensichtlichen

if(!FileExists(file)) {
    DefaultAction();
    return;
}
contents = OpenFile(file);
if(!SomeTest(contents))
{
    DefaultAction();
    return;
}        
DoSomething(contents);

Es scheint mir ziemlich normal? Für diese Art von großem Verfahren, bei dem viele kleine Dinge passieren müssen, deren Versagen letztere verhindern würde. Ausnahmen machen es ein bisschen sauberer, wenn das eine Option ist.

1
Steve Bennett

Natürlich können Sie in solchen Szenarien nur so weit gehen, aber hier ist ein Weg:

interface File<T> {
    function isOK():Bool;
    function getData():T;
}

var appleFile:File<Apple> = appleStorage.get(fileURI);
if (appleFile.isOK())
    eat(file.getData());
else
    cry();

Möglicherweise möchten Sie zusätzliche Filter. Dann mach das:

var appleFile = appleStorage.get(fileURI, isEdible);
//isEdible is of type Apple->Bool and will be used internally to answer to the isOK call
if (appleFile.isOK())
    eat(file.getData());
else
    cry();

Obwohl dies genauso gut sinnvoll sein könnte:

function eat(Apple:Apple) {
     if (isEdible(Apple)) 
         digest(Apple);
     else
         die();
}
var appleFile = appleStorage.get(fileURI);
if (appleFile.isOK())
    eat(appleFile.getData());
else
    cry();

Welches ist das Beste? Das hängt von dem Problem der realen Welt ab, mit dem Sie konfrontiert sind.
Aber das, was man mitnehmen sollte, ist: Mit Komposition und Polymorphismus kann man viel anfangen.

1
back2dos

Ihr Code ist hässlich, weil Sie in einer einzelnen Funktion zu viel tun. Sie möchten entweder die Datei verarbeiten oder die Standardaktion ausführen. Sagen Sie zunächst Folgendes:

if (!ProcessFile(file)) { 
  DefaultAction(); 
}

Perl und Ruby Programmierer schreiben processFile(file) || defaultAction()

Schreiben Sie nun ProcessFile:

if (FileExists(file)) { 
  contents = OpenFile(file);
  if (SomeTest(contents)) {
    processContents(contents);
    return true;
  }
}
return false;
1
kevin cline

Die eleganteste und prägnanteste Lösung ist natürlich die Verwendung eines Präprozessor-Makros.

#define DOUBLE_ELSE(CODE) else { CODE } } else { CODE }

So können Sie schönen Code wie folgt schreiben:

if(FileExists(file))
{
    contents = OpenFile(file);
    if(SomeTest(contents))
    {
        DoSomething(contents);
    }
    DOUBLE_ELSE(DefaultAction();)

Es kann schwierig sein, sich auf die automatische Formatierung zu verlassen, wenn Sie diese Technik häufig verwenden, und einige IDEs schreien Sie möglicherweise ein wenig an, was fälschlicherweise als fehlerhaft angenommen wird. Und wie das Sprichwort sagt, ist alles ein Kompromiss, aber ich nehme an, es ist kein schlechter Preis, um die Übel des wiederholten Codes zu vermeiden.

0
Peter Olson

So reduzieren Sie verschachtelte IF:

1/vorzeitige Rückkehr;

2/zusammengesetzter Ausdruck (kurzschlussbewusst)

Ihr Beispiel kann also folgendermaßen überarbeitet werden:

if( FileExists(file) && SomeTest(contents = OpenFile(file)) )
{
    DoSomething(contents);
    return;
}
DefaultAction();
0
DQ_vn

Ich stelle fest, dass dies eine alte Frage ist, aber ich habe ein Muster bemerkt, das nicht erwähnt wurde. Hauptsächlich wird eine Variable festgelegt, um später die Methode (n) zu bestimmen, die Sie aufrufen möchten (außerhalb von if ... else ...).

Dies ist nur ein weiterer Blickwinkel, um die Arbeit mit dem Code zu vereinfachen. Es ermöglicht auch, wenn Sie eine andere aufzurufende Methode hinzufügen oder die entsprechende Methode ändern möchten, die in bestimmten Situationen aufgerufen werden muss.

Anstatt alle Erwähnungen der Methode ersetzen zu müssen (und möglicherweise einige Szenarien zu verpassen), werden sie alle am Ende des if ... else ... -Blocks aufgelistet und sind einfacher zu lesen und zu ändern. Ich neige dazu, dies zu verwenden, wenn zum Beispiel mehrere Methoden aufgerufen werden können, aber innerhalb der verschachtelten, wenn ... sonst ... eine Methode in mehreren Übereinstimmungen aufgerufen werden kann.

Wenn Sie eine Variable festlegen, die den Status definiert, können Sie viele tief verschachtelte Optionen haben und den Status aktualisieren, wenn etwas ausgeführt werden soll (oder nicht).

Dies kann wie in dem Beispiel in der Frage verwendet werden, in der überprüft wird, ob "DoSomething" aufgetreten ist, und wenn nicht, führen Sie die Standardaktion aus. Oder Sie können den Status für jede Methode haben, die Sie aufrufen möchten, gegebenenfalls festlegen und dann die anwendbare Methode außerhalb von if ... else ... aufrufen.

Am Ende der verschachtelten if ... else ... -Anweisungen überprüfen Sie den Status und handeln entsprechend. Dies bedeutet, dass Sie nur eine einzige Erwähnung einer Methode anstelle aller Stellen benötigen, an denen sie angewendet werden soll.

bool ActionDone = false;

if (Method_1(object_A)) // Test 1
{
    result_A = Method_2(object_A); // Result 1

    if (Method_3(result_A)) // Test 2
    {
        Method_4(result_A); // Action 1
        ActionDone = true;
    }
}

if (!ActionDone)
{
    Method_5(); // Default Action
}
0
Steve Padmore

Ich habe viele Beispiele mit "return" gesehen, die ich auch verwende, aber manchmal möchte ich vermeiden, neue Funktionen zu erstellen, und stattdessen eine Schleife verwenden:

while (1) {
    if (FileExists(file)) {
        contents = OpenFile(file);
        if (SomeTest(contents)) {
           DoSomething(contents);
           break;
        } 
    }
    DefaultAction();
    break;
}

Wenn Sie weniger Zeilen schreiben möchten oder Endlosschleifen wie ich hassen, können Sie den Schleifentyp in "do ... while (0)" ändern und die letzte "Pause" vermeiden.

0
XzKto

Wie wäre es mit dieser Lösung:

content = NULL; //I presume OpenFile returns a pointer 
if(FileExists(file))
    contents = OpenFile(file);
if(content != NULL && SomeTest(contents))
    DoSomething(contents);
else
    DefaultAction();

Ich ging davon aus, dass OpenFile einen Zeiger zurückgibt, dies könnte jedoch auch mit der Rückgabe des Werttyps funktionieren, indem ein Standardwert angegeben wird, der nicht zurückgegeben werden kann (Fehlercodes oder ähnliches).

Natürlich erwarte ich keine mögliche Aktion über die SomeTest-Methode für den NULL-Zeiger (aber Sie wissen es nie), daher könnte dies auch als zusätzliche Überprüfung des NULL-Zeigers für den SomeTest-Aufruf (Inhalt) angesehen werden.

0
chedi