为什么要使用迭代而不是尾递归?

phi*_*ady 19 c# design-patterns

什么是设计气味,递归的不良做法?一旦我看到resharper暗示改进,我就快速浏览了谷歌.看到关于将尾部递归重新分解为迭代并将其称为设计气味的众多评论.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:获得C#编译器处理评论.看起来它现在是递归的
Target .net 4.5.C#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
Run Code Online (Sandbox Code Playgroud)

ILM*_*tan 20

因为人们错误地关注微优化而不是清晰可读的代码.

  • 我更喜欢DRY递归解决方案,易于阅读和维护,比节省几纳秒.请注意,凯文强调了一个非常优雅的迭代.一如既往,你可以编写好的和坏的递归.但是对于我来说,调用递归是一种难闻的气味.特别是当SOLID设计结果适用时.所以+1现在.在接受答案之前会等待看看会发生什么.谢谢ILMTitan (3认同)

sta*_*an0 8

递归使得附加(递归)函数调用,这也意味着一个堆栈分配,为每级(每一个对象,你处理).

通常,迭代更好,因为它不会进行额外的调用/分配.

当然,如果有许多对象需要处理,那么差异是可见的,我想在你的例子中并非如此.所以对你而言,这不是一个问题,我想这是为了教会你一般的更好的练习.