2013-08-22 18 views
16

Was ist der Design-Geruch, schlechte Übung in Rekursion? Sobald ich Resharper Verbesserungen sah sah ich schnell auf google. Ich habe zahlreiche Kommentare gesehen, wie ich die Tail-Rekursion zu Iterationen re-factorisieren und als Design-Geruch bezeichnen kann.Warum sollte die Iteration anstelle der Tail-Rekursion verwendet werden?

public static void DebugOutput2(Exception ex) { 
    if (ex == null) { 
     return; 
    } 
    Debug.WriteLine(ex.Message); 
    if (ex.InnerException != null) { 
     DebugOutput2(ex.InnerException); 
    } 
} 

// WAS REFACTORED TO 

public static void DebugOutput(Exception ex) { 
    if (ex == null) { 
     return; 
    } 
    while (true) { 
     Debug.WriteLine(ex.Message); 
     if (ex.InnerException != null) { 
      ex = ex.InnerException; 
      continue; 
     } 
     break; 
    } 
} 

EDIT: basierend auf C# Compiler Behandlung Kommentar. Sieht so aus, als ob es jetzt rekursiv ist
Target .net 4.5. C# 5.0

ILDASM Ausgang für Endrekursion Version: Zeigt rekursiven Aufruf und nicht die Iteration

.method public hidebysig static void DebugOutput(class [mscorlib]System.Exception ex) cil managed 
{ 
    // Code size  54 (0x36) 
    .maxstack 2 
    .locals init ([0] bool CS$4$0000) 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldnull 
    IL_0003: ceq 
    IL_0005: ldc.i4.0 
    IL_0006: ceq 
    IL_0008: stloc.0 
    IL_0009: ldloc.0 
    IL_000a: brtrue.s IL_000e 
    IL_000c: br.s  IL_0035 
    IL_000e: ldarg.0 
    IL_000f: callvirt instance string [mscorlib]System.Exception::get_Message() 
    IL_0014: call  void [System]System.Diagnostics.Debug::WriteLine(string) 
    IL_0019: nop 
    IL_001a: ldarg.0 
    IL_001b: callvirt instance class [mscorlib]System.Exception [mscorlib]System.Exception::get_InnerException() 
    IL_0020: ldnull 
    IL_0021: ceq 
    IL_0023: stloc.0 
    IL_0024: ldloc.0 
    IL_0025: brtrue.s IL_0035 
    IL_0027: nop 
    IL_0028: ldarg.0 
    IL_0029: callvirt instance class [mscorlib]System.Exception [mscorlib]System.Exception::get_InnerException() 
    IL_002e: call  void ca1.Program::DebugOutput(class [mscorlib]System.Exception) 
    IL_0033: nop 
    IL_0034: nop 
    IL_0035: ret 

} // end of method Program::DebugOutput 
+1

Beachten Sie, dass, während die CLR Tailaufrufe unterstützt, [der C# -Compiler gibt sie nicht aus] (http://www.thomaslevesque.com/2011/09/02/tail-recursion-in-c/) (letztes Mal habe ich überprüft). In jedem Fall ist es unwahrscheinlich, dass Sie den Stack mit diesem speziellen Konstrukt sprengen werden, und die Performance ist kein wirkliches Problem, da Sie sowieso ohnehin mit Ausnahmen arbeiten. –

+4

Ich würde diese "while" -Schleife zugunsten von etwas zurückweisen, das näher zu 'while (ex! = Null) {Debug.WriteLine (ex.Message); ex = ex.InnerException}' ist. – Kevin

+0

Überlegen Sie, was mit der Tail-Rekursion in Bezug auf die Leistung geschieht. Wir müssen einen Stapelrahmen erstellen, einige Dinge kopieren und dann die Funktion aufrufen. Abhängig von der Rekursion kann dies zu einem geblasenen Stack führen (obwohl nicht in diesem Beispiel wahrscheinlich) und sicherlich schlechter Performance, die nicht-rekursive Iteration – rileyberton

Antwort

16

Weil die Menschen mehr fälschlicherweise kümmern sich um Mikro-Optimierungen als klar und lesbaren Code.

+2

Ich bevorzuge DRY rekursive Lösung, die einfach zu lesen und zu pflegen ist als die Einsparung ein paar Nanosekunden. Wohlgemerkt, eine sehr elegante Iteration wurde von Kevin hervorgehoben. Wie immer können Sie gute und schlechte Rekursionen schreiben. Aber die Rekursion, ein schlechter Geruch, erscheint mir übertrieben. Besonders wenn SOLID Design-Ergebnisse zutreffen. Also jetzt +1. Ich werde abwarten, was passiert, bevor ich als Antwort akzeptiere. Danke ILMTitan –

+0

Sie konnten die redundante Nullprüfung auf InnerException in der rekursiven Lösung auch entfernen. – ILMTitan

8

Die recursion macht zusätzlichen (rekursiven) Funktionsaufruf, was auch eine Stapelzuweisung bedeutet, für jede Ebene (jedes Objekt, das Sie behandeln).

Im Allgemeinen ist die Iteration better, weil es diesen zusätzlichen Aufruf/Zuweisung nicht macht.

Natürlich wäre der Unterschied sichtbar sein, falls es viele Objekte zu handhaben, die ich nehme nicht der Fall in Ihrem Beispiel. Für dich ist das kein Problem, und ich denke, es ist die Absicht, dir eine bessere Übung im Allgemeinen beizubringen.

Verwandte Themen