我怎样才能在递归代码上捕获stackoverflow异常的根

时间:2010-07-13 12:17:55

标签: c# stack-overflow recursion

我有以下递归代码,我得到一个stackoverflow异常。我无法弄清楚根本原因,因为一旦我得到异常,我就不会在Visual Studio中获得完整的调用堆栈。

这个想法是有组织团队加入更大的“主要”团队。

有没有人看到下面这段代码的缺陷可能是罪魁祸首?

    private Unit GetUnit(Unit organisationalUnit)
    {
        if (organisationalUnit.IsMainUnit)
        {
            return organisationalUnit;                
        }

        if (organisationalUnit.Parent == null)
            return null;

        return GetUnit(organisationalUnit.Parent);
    }

9 个答案:

答案 0 :(得分:5)

  1. 你可能有一个单位是它自己的父母,或者你可能有一个父母的循环(例如A-B-C-A)。也就是说,问题可能出在数据上,而不是代码本身。
  2. 确认IsMainUnitParent的getter不会调用GetUnit

答案 1 :(得分:4)

根始终有Parent == null吗? 试过检查

if (organisationalUnit.Parent == organisationalUnit)
    return null;

答案 2 :(得分:2)

你可以尝试这个来更好地调试它。它不会影响您的生产代码。

using System.Diagnostics;

private Unit GetUnit(Unit organisationalUnit, int depth)
{
    debug.assert(depth < 10, "Reached an unexpected high recursion depth"); 

    if (organisationalUnit.IsMainUnit)
    {
        return organisationalUnit;                
    }

    if (organisationalUnit.Parent == null)
        return null;

    return GetUnit(organisationalUnit.Parent, depth + 1);
}

private Unit GetUnit(Unit organisationalUnit)
{
    return GetUnit(organisationalUnit.Parent, 0);
}

第二个想法......

最有可能的是你在某个地方有一个循环引用。

A.parent = B;
B.parent = C;
C.parent = A;

您可以尝试传递一组以前访问过的节点,并检查您之前是否访问过该节点。

递归的是必须确定它将结束,并且未经检查的循环引用是不会结束的情况。

答案 3 :(得分:1)

有没有理由不将其重新编码为迭代?这样,为了捕获不良(循环)数据,更容易对组织树的深度设置硬限制。

递归很有趣,尾部优化甚至可以使它高效,但在这里它看起来像一个小问题的大锤子。

答案 4 :(得分:1)

我对视觉工作室了解不多。 但你应该检查是否复发。 e.g。

private Unit GetUnit(Unit organisationalUnit)
{
      GetUnit(organisationalUnit, new vector());
}

private Unit GetUnit(Unit organisationalUnit,vector x)
{
    if (organisationalUnit.IsMainUnit)
    {
        return organisationalUnit;                
    }

    if (organisationalUnit.Parent == null)
        return null;

    x.add(this);
    if(x.contains(organisationalUnit.Parent)) throw new Exception("recurrent parent");
    return GetUnit(organisationalUnit.Parent,x);
}

答案 5 :(得分:1)

这样做的一种方法是减少堆栈大小,以便它可以更早崩溃。

你可以通过在程序开头浪费帧来做到这一点,即得到如下的堆栈跟踪:f_n,f_(n-1),...,f_1,waste,waste ,...,废物,如(在C伪代码中)

int wasted = 1;

废物(int n,void(* f)()) {if(n> 0)waste(n - 1,f)else f();   浪费+ = 1; }

main(){waste(N,mainprime); }

其中mainprime是您的旧主页,N足够大,可以达到您想要的f_1。

答案 6 :(得分:0)

代码看起来不错,也许你在单元树中有一些封闭的循环?尝试使用organisationalUnit.GetHashCode值添加跟踪线,跟踪输出不受限制,可以帮助检测堆栈溢出原因。

答案 7 :(得分:0)

我猜你可以通过重写

来避免递归
while(organisationalUnit!=null && !organisationalUnit.IsMainUnit)
  organisationalUnit=organisationalUnit.Parent;

return organisationalUnit;

我希望有所帮助。

编辑:我刚刚意识到,如果你有某种循环依赖,这仍然会失败。

答案 8 :(得分:0)

你的图表中可能有一个循环(参见,它不是树)。

您可以使用这样的代码来检测它:

private Unit GetUnit(Unit organisationalUnit) {
  return GetUnit(organisationalUnit, new HashSet<Unit>());
}

private Unit GetUnit(Unit organisationalUnit, HashSet<Unit> visited) {
  if (visited.Contains(organisationalUnit)) {
    throw new Exception("Cycle detected!"); // or just return null if you prefer
  }
  visited.Add(organisationalUnit);

  if (organisationalUnit.IsMainUnit) {
    return organisationalUnit;
  }

  if (organisationalUnit.Parent == null)
    return null;

  return GetUnit(organisationalUnit.Parent, visited);
}