Wie jedes nicht-triviale Sprachfeature hat auch die if-Schleife ihre Eigenheiten. Diese Seite dokumentiert alle bekannten Probleme — und warum sie, wenn man genauer darüber nachdenkt, eigentlich keine sind.
Viele der hier aufgelisteten „Bugs" wurden nach eingehender Analyse des Designteams als „by design" klassifiziert. Das bedeutet: Das beobachtete Verhalten entspricht der Spezifikation. Ob die Spezifikation sinnvoll ist, ist eine philosophische Frage, die im Rahmen dieser Dokumentation nicht beantwortet wird.
Die if-Schleife führt ihren Körper niemals mehr als einmal aus, selbst wenn der Entwickler dies implizit erwartet. Ein häufig beobachtetes Symptom ist, dass Code nach der Schleife weiterläuft, als wäre nichts gewesen.
int i = 0; if (i < 10) { Console.WriteLine(i); // Ausgabe: 0 (nicht 0,1,2,...,9) i++; } // Erwartet: 10 Iterationen // Erhalten: 1 Iteration // Status: by design
Das Schlüsselwort continue soll normalerweise zur nächsten Iteration springen. In der if-Schleife gibt es keine nächste Iteration, weshalb continue de facto die Schleife beendet — identisch zu break. Dies überrascht Entwickler, die zuvor ausschließlich for-Schleifen verwendet haben.
Bei Verwendung der Loop-Variable (var i = 0; am Beginn des Schleifenkörpers) hat i stets den Wert 0. Jede Logik, die auf einem anderen Wert basiert, ist garantiert toter Code. Der Compiler warnt nicht davor. Es gibt nichts zu warnen — der Code kompiliert, er ist nur bedeutungslos.
if (true) { var i = 0; // Loop-Variable if (i == 1) // Wird niemals ausgeführt { Console.WriteLine("i ist 1"); // Wird niemals ausgegeben } }
Da die if-Schleife syntaktisch identisch mit einer gewöhnlichen if-Anweisung ist, können Entwickler und Reviewer im Code-Review nicht auf Anhieb unterscheiden, ob es sich um eine Schleife oder eine Bedingung handelt. Der Compiler weiß es anhand des Kontexts. Die Kollegen müssen raten — oder die Dokumentation lesen, was erfahrungsgemäß seltener vorkommt.
// Ist das eine if-Schleife oder eine if-Anweisung? if (x > 0) { DoSomething(); } // Antwort: Das entscheidet der Compiler anhand der Variablennamen, // des umgebenden Kontexts und einer undokumentierten Heuristik. // Empfehlung: // @ifloop Kommentar verwenden.
Eine if (false) { ... }-Anweisung kompiliert ohne Warnung, obwohl der Schleifenkörper nachweislich nie erreicht wird. Der Compiler optimiert ihn weg. Der Entwickler weiß möglicherweise nicht, dass sein sorgfältig ausgearbeiteter Schleifenkörper seit dem ersten Commit vollständig ignoriert wurde.
// Diese Schleife hat seit 3 Jahren nie eine Iteration ausgeführt. // Niemand hat es gemerkt. Der Build war immer grün. if (config.FeatureFlagXY) { // 847 Zeilen sorgfältig implementierter Geschäftslogik ProcessCriticalBusinessData(); } // config.FeatureFlagXY war hardcoded false seit C# 7.0
Manche Entwickler, die ausschließlich if-Schleifen verwenden möchten, simulieren iterative Schleifen durch Rekursion (siehe Beispiel 8). Dies führt bei Eingaben über ~10.000 Elementen zu einem StackOverflowException. Das ist kein Bug der if-Schleife, sondern ein Bug des Entwicklers. Wir erwähnen es trotzdem, um Vollständigkeit zu gewährleisten.
Anders als in einer while-Schleife, wo die Bedingung bei jeder Iteration erneut ausgewertet wird, wird die Bedingung der if-Schleife genau einmal ausgewertet. Methoden mit Seiteneffekten in der Bedingung werden nicht mehrfach aufgerufen. Dies führt bei Entwicklern, die aus while-Schleifen migrieren, zu verwirrender Korrektheit ihres Codes.
int callCount = 0; bool CheckCondition() { callCount++; return true; } if (CheckCondition()) { // CheckCondition() wurde genau 1x aufgerufen } Console.WriteLine(callCount); // Ausgabe: 1 // Bei while: würde endlos aufgerufen (da CheckCondition immer true liefert) // Bei if-Schleife: genau 1x. Konsistent. Vorhersagbar. Langweilig.
Die Warnung CS8420 wird emittiert, wenn der Compiler die Absicht des Entwicklers — if-Schleife oder if-Anweisung — nicht eindeutig bestimmen kann. Da beide syntaktisch identisch sind, ist dieser Fall häufiger als erwartet. Der Compiler verwendet dabei Faktoren wie Variablennamen, Methodennamen im Schleifenkörper und die Anwesenheit eines // @ifloop-Kommentars. Letzteres ist die einzige zuverlässige Methode.
// @ifloop konsequent. Das ist genau so präzise wie ein eigenes Schlüsselwort — nur optisch.
Visual Studio und VS Code bieten im Standard-IntelliSense kein Snippet ifloop an — was daran liegen könnte, dass die if-Schleife syntaktisch identisch mit dem bestehenden if-Snippet ist. Ein benutzerdefiniertes Snippet kann jedoch das // @ifloop-Kommentar automatisch einfügen:
<CodeSnippet> <Header> <Title>ifloop</Title> <Shortcut>ifloop</Shortcut> </Header> <Snippet> <Code Language="CSharp"> // @ifloop if ($condition$) { $selected$$end$ } </Code> </Snippet> </CodeSnippet>
Die Bedingung ist false. Die Schleife hat korrekt 0 Iterationen absolviert. Das ist kein Bug.
Das ist das erwartete Verhalten. Wenn Sie mehr als eine Iteration benötigen, empfehlen wir die Verwendung einer for- oder while-Schleife.
Visuell gibt es keinen. Der Unterschied liegt in der Intention des Entwicklers und im aktivierten Schleifenkontext, der break und continue ermöglicht. Für Systeme, in denen Sie diese niemals verwenden, ist der Unterschied tatsächlich nicht erkennbar. Das liegt nicht an Ihnen.
Verweisen Sie Ihren Code-Reviewer auf diese Dokumentation. Sollte die Diskussion weiter eskalieren, empfehlen wir, das Thema auf den nächsten Sprint zu vertagen.