Ответ 1
Потому что люди ошибочно больше заботятся о микро-оптимизации, чем чистый и читаемый код.
Что такое дизайнерский запах, плохая практика в рекурсии? как только я увидел 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
Потому что люди ошибочно больше заботятся о микро-оптимизации, чем чистый и читаемый код.
recursion выполняет дополнительный (рекурсивный) вызов функции, что также означает распределение стека для каждого уровня (каждый объект, который вы обрабатываете).
Как правило, итерация лучше, поскольку она не делает этого дополнительного вызова/выделения.
Конечно, разница будет видна в случае, если есть много объектов, которые, я полагаю, не так в вашем примере. Поэтому для вас это не проблема, и я предполагаю, что намерение состоит в том, чтобы научить вас лучшей практике вообще.