it-swarm-eu.dev

Sollte ich frühzeitig von einer Funktion zurückkehren oder eine if-Anweisung verwenden?

Ich habe diese Art von Funktion oft in beiden Formaten geschrieben und mich gefragt, ob und warum ein Format einem anderen vorgezogen wird.

public void SomeFunction(bool someCondition)
{
    if (someCondition)
    {
        // Do Something
    }
}

oder

public void SomeFunction(bool someCondition)
{
    if (!someCondition)
        return;

    // Do Something
}

Normalerweise codiere ich mit dem ersten, da mein Gehirn beim Codieren so arbeitet, obwohl ich denke, dass ich den zweiten bevorzuge, da er sich sofort um die Fehlerbehandlung kümmert und ich leichter lesen kann

302
Rachel

Ich bevorzuge den zweiten Stil. Schieben Sie zuerst ungültige Fälle aus dem Weg, indem Sie entweder einfach beenden oder Ausnahmen auslösen, eine leere Zeile einfügen und dann den "echten" Hauptteil der Methode hinzufügen. Ich finde es leichter zu lesen.

400
Mason Wheeler

Auf jeden Fall letzteres. Ersteres sieht momentan nicht schlecht aus, aber wenn Sie komplexeren Code erhalten, kann ich mir nicht vorstellen, dass jemand dies denken würde:

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

ist besser lesbar als

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}

Ich gebe voll und ganz zu, dass ich den Vorteil einzelner Austrittspunkte nie verstanden habe.

169
Jason Viers

Es kommt darauf an - Im Allgemeinen werde ich mir nicht die Mühe machen, eine Menge Code zu verschieben, um die Funktion frühzeitig zu beenden - der Compiler wird sich im Allgemeinen darum kümmern. Das heißt, wenn es oben einige grundlegende Parameter gibt, die ich brauche und die ich sonst nicht fortsetzen kann, werde ich früh ausbrechen. Wenn eine Bedingung einen riesigen if -Block in der Funktion erzeugt, wird dieser ebenfalls als Folge davon früh ausbrechen.

Das heißt, wenn eine Funktion beim Aufrufen einige Daten benötigt, werde ich normalerweise eine Ausnahme auslösen (siehe Beispiel), anstatt nur zurückzukehren.

public int myFunction(string parameterOne, string parameterTwo) {
  // Can't work without a value
  if (string.IsNullOrEmpty(parameterOne)) {
    throw new ArgumentNullException("parameterOne");
  } 
  if (string.IsNullOrEmpty(parameterTwo)) {
    throw new ArgumentNullException("parameterTwo");
  }

  // ...      
  // Do some work
  // ...

  return value;
}
32
rjzii

Ich bevorzuge die vorzeitige Rückkehr.

Wenn Sie einen Einstiegspunkt und einen Ausstiegspunkt haben, müssen Sie immer den gesamten Code in Ihrem Kopf bis zum Ausstiegspunkt verfolgen (Sie wissen nie, ob ein anderer Code unten etwas anderes mit dem Ergebnis tut, also Sie müssen es verfolgen, bis die existieren). Sie tun dies unabhängig davon, welcher Zweig das Endergebnis bestimmt. Dies ist schwer zu folgen.

Wenn ein Eintrag vorhanden ist und mehrere vorhanden sind, kehren Sie zurück, wenn Sie Ihr Ergebnis haben, und verfolgen es nicht bis zum Ende, um zu sehen, dass niemand etwas anderes daran tut (da es seit Ihrer Rückkehr nichts anderes mehr gibt). Es ist, als würde der Methodenkörper in mehrere Schritte aufgeteilt, wobei jeder Schritt die Möglichkeit bietet, das Ergebnis zurückzugeben oder den nächsten Schritt sein Glück versuchen zu lassen.

24
user7197

In der C-Programmierung, in der Sie manuell bereinigen müssen, gibt es viel zu sagen für die Ein-Punkt-Rückgabe. Selbst wenn es momentan nicht erforderlich ist, etwas zu bereinigen, kann jemand Ihre Funktion bearbeiten, etwas zuweisen und es vor der Rückkehr bereinigen. In diesem Fall ist es ein Albtraum, alle Rückgabeanweisungen durchzusehen.

In der C++ - Programmierung gibt es Destruktoren und sogar jetzt Scope-Exit-Guards. All dies muss vorhanden sein, um sicherzustellen, dass der Code in erster Linie ausnahmesicher ist. Daher ist der Code gut gegen vorzeitiges Beenden geschützt und hat daher keinen logischen Nachteil und ist lediglich ein Stilproblem.

Ich weiß nicht genug über Java, ob "endlich" Blockcode aufgerufen wird und ob Finalizer mit der Situation umgehen können, dass etwas passieren muss.

C # Ich kann auf keinen Fall antworten.

Die D-Sprache bietet Ihnen die richtigen integrierten Schutzfunktionen für den Scope-Exit und ist daher gut auf den frühen Exit vorbereitet. Sie sollte daher kein anderes Problem als den Stil darstellen.

Funktionen sollten natürlich nicht so lange dauern, und wenn Sie eine große switch-Anweisung haben, wird Ihr Code wahrscheinlich auch schlecht berücksichtigt.

13
CashCow

Ich benutze beide.

Wenn DoSomething 3-5 Codezeilen enthält, sieht der Code mit der ersten Formatierungsmethode einfach gut aus.

Aber wenn es viel mehr Zeilen als das hat, dann bevorzuge ich das zweite Format. Ich mag es nicht, wenn sich die öffnenden und schließenden Klammern nicht auf demselben Bildschirm befinden.

9
azheglov

Frühe Rückkehr für den Sieg. Sie können hässlich erscheinen, aber viel weniger hässlich als große if Wrapper, insbesondere wenn mehrere Bedingungen überprüft werden müssen.

9
STW

Ein klassischer Grund für Single-Entry-Single-Exit ist, dass die formale Semantik ansonsten unbeschreiblich hässlich wird (der gleiche Grund, warum GOTO als schädlich angesehen wurde).

Anders ausgedrückt, es ist einfacher zu überlegen, wann Ihre Software die Routine verlässt, wenn Sie nur 1 Rückgabe haben. Welches ist auch ein Argument gegen Ausnahmen.

Normalerweise minimiere ich den Early-Return-Ansatz.

8
Paul Nathan

Persönlich bevorzuge ich zu Beginn Pass/Fail-Zustandsprüfungen. Dadurch kann ich die meisten der häufigsten Fehler oben in der Funktion mit dem Rest der folgenden Logik gruppieren.

7
nathan

Es hängt davon ab, ob.

Vorzeitige Rückkehr, wenn eine offensichtliche Sackgasse vorliegt, auf die sofort geprüft werden muss, um den Rest der Funktion sinnlos zu machen. *

Stellen Sie Retval + Single Return ein, wenn die Funktion komplexer ist und andernfalls mehrere Austrittspunkte haben könnte (Lesbarkeitsproblem).

* Dies kann häufig auf ein Entwurfsproblem hinweisen. Wenn Sie feststellen, dass viele Ihrer Methoden einen externen/Parameterstatus oder dergleichen überprüfen müssen, bevor der Rest des Codes ausgeführt wird, sollte dies wahrscheinlich vom Aufrufer behandelt werden .

6
Bobby Tables

Verwenden Sie ein If

In Don Knuths Buch über GOTOs habe ich ihn gelesen und einen Grund angegeben, warum in einer if-Aussage immer die wahrscheinlichste Bedingung an erster Stelle steht. Unter der Annahme, dass dies immer noch eine vernünftige Idee ist (und nicht aus reiner Rücksicht auf die Geschwindigkeit der Ära). Ich würde sagen, frühe Rückgaben sind keine gute Programmierpraxis, insbesondere angesichts der Tatsache, dass sie häufig zur Fehlerbehandlung verwendet werden, es sei denn, Ihr Code schlägt eher fehl als nicht :-)

Wenn Sie den obigen Ratschlägen folgen, müssen Sie diese Rückgabe am Ende der Funktion einfügen, und dann können Sie sie dort genauso gut nicht einmal als Rückgabe bezeichnen. Legen Sie einfach den Fehlercode fest und geben Sie sie in zwei Zeilen zurück. Dadurch wird das Ideal 1 Eingang 1 Ausgang erreicht.

Delphi-spezifisch ...

Ich bin der Meinung, dass dies eine gute Programmierpraxis für Delphi-Programmierer ist, obwohl ich keinen Beweis habe. Vor D2009 haben wir keine atomare Möglichkeit, einen Wert zurückzugeben, wir haben exit; und result := foo; oder wir könnten einfach Ausnahmen werfen.

Wenn Sie ersetzen müssten

if (true) {
 return foo;
} 

for

if true then 
begin
  result := foo; 
  exit; 
end;

sie könnten es einfach satt haben, das ganz oben auf jeder Ihrer Funktionen zu sehen und es vorziehen

if false then 
begin
  result := bar;

   ... 
end
else
   result := foo;

und vermeiden Sie einfach exit insgesamt.

3
Peter Turner

Ich stimme der folgenden Aussage zu:

Ich persönlich bin ein Fan von Schutzklauseln (das zweite Beispiel), da sie das Einrücken der Funktion reduzieren. Einige Leute mögen sie nicht, weil sie zu mehreren Rückgabepunkten der Funktion führen, aber ich denke, dass es bei ihnen klarer ist.

Entnommen aus diese Frage im Stackoverflow .

2
Toto

Ich würde lieber schreiben:

if(someCondition)
{
    SomeFunction();
}
1
Josh K

Wie Sie schreibe ich normalerweise den ersten, bevorzuge aber den letzten. Wenn ich viele verschachtelte Prüfungen habe, überarbeite ich normalerweise die zweite Methode.

Mir gefällt nicht, wie die Fehlerbehandlung von der Prüfung wegbewegt wird.

if not error A
  if not error B
    if not error C
      // do something
    else handle error C
  else handle error B
else handle error A

Ich bevorzuge das:

if error A
  handle error A; return
if error B
  handle error B; return
if error C
  handle error C; return

// do something
1
jolt

Ich verwende heutzutage fast ausschließlich frühe Renditen, bis zum Äußersten. Ich schreibe das

self = [super init];

if (self != nil)
{
    // your code here
}

return self;

wie

self = [super init];
if (!self)
    return;

// your code here

return self;

aber es ist wirklich egal. Wenn Ihre Funktionen mehr als eine oder zwei Verschachtelungsebenen enthalten, müssen diese zerstört werden.

1
Dan Rosenstark

Die Bedingungen oben werden als "Voraussetzungen" bezeichnet. Indem Sie if(!precond) return; setzen, listen Sie alle Voraussetzungen visuell auf.

Die Verwendung des großen "if-else" -Blocks kann den Einzugskopf erhöhen (ich habe das Zitat über Einrückungen mit drei Ebenen vergessen).

0
Ming-Tang