Jaki jest zapach projektu, słaba praktyka rekursji? kiedy zobaczyłem Resharpera sugerującego ulepszenia, szybko rozejrzałem się po Google. Zobaczyłem wiele komentarzy dotyczących re-factoring rekurencji ogona do iteracji i odnoszących się do niego jako zapach projektu.Dlaczego zamiast rekurencji ogona należy użyć iteracji?
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: w oparciu o C# kompilator leczenia komentarza. Wygląda na to, że teraz jest rekurencyjny.
Target .net 4.5. C# 5.0
ILDASM wyjściowy dla wersji ogon rekurencji: Pokazuje rekurencyjnej wezwanie i nie iteracji
.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
Należy pamiętać, że choć CLR obsługuje wywołania ogona, [kompilator C# ich nie emituje] (http://www.thomaslevesque.com/2011/09/02/tail-recursion-in-c/) (ostatni raz sprawdziłem). W każdym razie jest mało prawdopodobne, że wysadzisz stos tą konstrukcją, a wydajność nie jest tak naprawdę problemem, ponieważ i tak już masz do czynienia z wyjątkami. –
Odrzuciłbym pętlę 'while' na rzecz czegoś bliższego' while (ex! = Null) {Debug.WriteLine (ex.Message), ex = ex.InnerException} '. – Kevin
Zastanów się, co się dzieje, używając rekurencji ogona z perspektywy wydajności. Musimy stworzyć ramkę stosu, skopiować pewne rzeczy i zadzwonić, a następnie wywołać funkcję. W zależności od rekurencji może to prowadzić do dmuchanego stosu (chociaż nie w tym przykładzie, prawdopodobnie) i na pewno gorszej wydajności, jaką jest nierekurencyjna iteracja. – rileyberton