Почему вместо хвостовой рекурсии следует использовать итерацию?

Что такое дизайнерский запах, плохая практика в рекурсии? как только я увидел resharper, предлагая улучшения, я быстро оглянулся на google. Увидел многочисленные комментарии о повторной факторизации хвостовой рекурсии к итерациям и ссылался на нее как на запах дизайна.

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;
    }
}

РЕДАКТИРОВАТЬ: Комментарий к компилятору BASed на С#. Похоже, что сейчас рекурсивный
Target.net 4.5. С# 5.0

ILDASM Выход для версии рекурсии хвоста: показывает рекурсивный вызов, а не итерацию

.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

Потому что люди ошибочно больше заботятся о микро-оптимизации, чем чистый и читаемый код.

Ответ 2

recursion выполняет дополнительный (рекурсивный) вызов функции, что также означает распределение стека для каждого уровня (каждый объект, который вы обрабатываете).

Как правило, итерация лучше, поскольку она не делает этого дополнительного вызова/выделения.

Конечно, разница будет видна в случае, если есть много объектов, которые, я полагаю, не так в вашем примере. Поэтому для вас это не проблема, и я предполагаю, что намерение состоит в том, чтобы научить вас лучшей практике вообще.