8 Stimmen

Ist goto ok für den Ausbruch aus verschachtelten Schleifen?

JavaScript unterstützt eine goto-ähnliche Syntax zum Verlassen von verschachtelten Schleifen. Im Allgemeinen ist das keine gute Idee, aber es wird als akzeptable Praxis angesehen. C# unterstützt nicht direkt die break labelName Syntax...aber es unterstützt die berüchtigte goto .

Ich glaube, dass das Gleiche in C# erreicht werden kann:

    int i = 0;            
    while(i <= 10)
    {
        Debug.WriteLine(i);
        i++;
        for(int j = 0; j < 3; j++)
            if (i > 5)
            {
                goto Break;//break out of all loops
            }
    }

    Break:

Ist das Szenario der verschachtelten Schleife nach derselben Logik wie bei JavaScript eine akzeptable Verwendung von goto ? Andernfalls ist die einzige mir bekannte Möglichkeit, diese Funktionalität zu erreichen, das Setzen einer bool mit angemessenem Umfang.

29voto

Eric Lippert Punkte 628543

Meine Meinung: Komplexe Codeabläufe mit verschachtelten Schleifen sind schwer zu durchdenken; Verzweigungen, sei es mit goto oder break, machen es nur schwieriger. Anstatt das goto zu schreiben, würde ich zuerst gründlich darüber nachdenken, ob es eine Möglichkeit gibt, die verschachtelten Schleifen zu eliminieren.

Ein paar nützliche Techniken:

Erste Technik: Umwandlung der inneren Schleife in eine Methode. Die Methode soll zurückgeben, ob die äußere Schleife verlassen werden soll oder nicht. So:

for(outer blah blah blah)
{
    for(inner blah blah blah)
    {
        if (whatever)
        {
             goto leaveloop;      
        }
    }
}
leaveloop:    
...

wird

for(outer blah blah blah)
{
    if (Inner(blah blah blah))
        break;
}

...

bool Inner(blah blah blah)
{
    for(inner blah blah blah)
    {
        if (whatever)
        {
             return true;      
        }
    }
    return false;
}

Zweite Technik: Wenn die Schleifen keine Seiteneffekte haben, verwenden Sie LINQ.

// fulfill the first unfulfilled order over $100
foreach(var customer in customers)
{
    foreach(var order in customer.Orders)
    {
        if (!order.Filled && order.Total >= 100.00m)
        {
             Fill(order);
             goto leaveloop;      
        }
    }
}
leaveloop:    

schreiben Sie stattdessen:

var orders = from customer in customers
             from order in customer.Orders;
             where !order.Filled
             where order.Total >= 100.00m
             select order;
var orderToFill = orders.FirstOrDefault();
if (orderToFill != null) Fill(orderToFill);

Keine Schleifen, also kein Ausbrechen erforderlich.

Alternativ können Sie, wie der Konfigurator in einem Kommentar anmerkt, den Code auch in dieser Form schreiben:

var orderToFill = customers
    .SelectMany(customer=>customer.Orders)
    .Where(order=>!order.Filled)
    .Where(order=>order.Total >= 100.00m)
    .FirstOrDefault();
if (orderToFill != null) Fill(orderToFill);

Die Moral von der Geschicht': Schleifen betonen den Kontrollfluss auf Kosten der Geschäftslogik . Anstatt zu versuchen, immer komplexere Kontrollabläufe übereinander zu stapeln, sollten Sie versuchen, den Code so umzugestalten, dass die Geschäftslogik klar ist.

11voto

slugster Punkte 48412

Lassen Sie uns eines klarstellen: Es ist nichts grundsätzlich falsch daran, die goto Aussage ist sie nicht böse - sie ist nur ein weiteres Werkzeug im Werkzeugkasten. Sie ist wie Sie es verwenden die wirklich wichtig ist, und sie wird leicht missbraucht.

Der Ausbruch aus einer verschachtelten Schleife kann eine gültige Verwendung der Anweisung sein, obwohl Sie zunächst prüfen sollten, ob sie umgestaltet werden kann. Können Ihre Schleifenausdrücke umgeschrieben werden? Verwenden Sie den richtigen Schleifentyp? Können Sie die Liste der Daten, über die Sie iterieren, so filtern, dass Sie nicht vorzeitig abbrechen müssen? Sollten Sie einen Teil des Schleifencodes in eine separate Funktion umstrukturieren?

5voto

ThiefMaster Punkte 297146

IMO ist es in Sprachen akzeptabel, die keine Unterstützung für break n; donde n gibt die Anzahl der Schleifen an, die abgebrochen werden sollen.
Zumindest ist es viel lesbarer, als eine Variable zu setzen, die dann in der äußeren Schleife überprüft wird.

2voto

tier1 Punkte 6153

Ich glaube, dass das "goto" in dieser Situation akzeptabel ist. C# unterstützt leider keine raffinierten Möglichkeiten, um aus verschachtelten Schleifen auszubrechen.

2voto

Jesse C. Slicer Punkte 19426

In C# ist das eine inakzeptable Praxis. Wenn es keine Möglichkeit gibt, Ihr Design kann es vermeiden, gut, muss es verwenden. Aber schöpfen Sie zuerst alle anderen Alternativen aus. Es wird für eine bessere Lesbarkeit und Wartbarkeit sorgen. Für Ihr Beispiel habe ich ein solches mögliches Refactoring entworfen:

void Original()
{
    int i = 0;            
    while(i <= 10)
    {
        Debug.WriteLine(i);
        i++;
        if (Process(i))
        {
            break;
        }
    }
}

bool Process(int i)
{
    for(int j = 0; j < 3; j++)
        if (i > 5)
        {
            return true;
        }
    return false;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X