【问题标题】:C# - Property is causing StackOverflowC# - 属性导致 StackOverflow
【发布时间】:2012-08-22 20:38:23
【问题描述】:
public class ModelInfo
{
    public int AssignedCount { get; set; }
    public int UnassignedCount { get; set; }
    public int TotalCount { get { return UnassignedCount + TotalCount; } }
}

*编辑:* 当我将此代码放入 SO 时,我意识到 TotalCount 属性正在添加 UnassignedCount + TotalCount (我的意思是将其他两个计数加在一起)。有人可以详细解释为什么会发生 SO 错误吗?我的意思是,低级的东西。

堆栈溢出!

【问题讨论】:

  • 你想知道为什么会发生堆栈溢出吗?无限递归就是答案。 TotalCount“调用”TotalCount“调用”TotalCount……你明白了。

标签: c# stack-overflow


【解决方案1】:

你是从TotalCount 内部调用TotalCount,不要那样做。

您可以为属性值设置另一个字段。

不过,我怀疑您的代码应该是:

public int TotalCount { get { return UnassignedCount + AssignedCount ; } }

编辑: 至于为什么会出现stackoverflow,那是因为当您使用Properties 时,.NET 编译器实际上会生成2 个函数,set_PropertyNameget_PropertyName。所以从本质上讲,它会导致get_PropertyName 方法调用的stackoverflow 变得无限深。

【讨论】:

    【解决方案2】:

    查看正在发生的事情 (IMO) 的最简单方法是将这些属性转换为方法:

    // If we didn't have properties, this is what the two first lines would be. Ick!
    private int assignedCount;
    private int unassignedCount;
    
    public int GetAssignedCount()
    {
        return assignedCount;
    }
    
    public void SetAssignedCount(int value)
    {
        assignedCount = value;
    }
    
    public int GetUnassignedCount()
    {
        return unassignedCount;
    }
    
    public void SetUnassignedCount(int value)
    {
        unssignedCount = value;
    }
    
    // And here's the read-only TotalCount property translation
    public int GetTotalCount()
    {
        return GetUnassignedCount() + GetTotalCount();
    }
    

    现在GetTotalCount() 中的递归应该很清楚了。该方法无条件调用自身,因此最终会炸毁堆栈。

    混合自动实现的属性以及属性访问看起来类似于字段访问的事实有时会妨碍记住它们实际上只是伪装的方法。希望上面的翻译能让这一切更加明显。

    【讨论】:

      【解决方案3】:

      有人可以详细解释为什么会发生 SO 错误吗?

      当然:为了计算TotalCount,编译器生成如下代码:

      • 获取UnassignedCount
      • 获取TotalCount
      • UnassignedCount 添加到TotalCount
      • 返回结果

      当调用TotalCount 的属性getter 时(请记住,getter 是一个使用特殊语法的常规无参数函数),运行时将返回地址放在堆栈中。第二步找到我们回到开头,堆栈上有一个额外的返回地址;第三步再做一次,然后是第四步,第五步,以此类推。每次调用都会在堆栈上存放另一个返回地址。这一直持续到堆栈的极限,此时抛出异常。

      【讨论】:

        【解决方案4】:

        你应该可以这样写: 您应该注意, TotalCount 永远不能设置为,因此它没有任何价值。你是不是有意这样做:

        public class ModelInfo
        {
            public int AssignedCount { get; set; }
            public int UnassignedCount { get; set; }
            public int TotalCount { get { return UnassignedCount + AssignedCount; } }
        }
        

        【讨论】:

          【解决方案5】:

          有一种称为“堆栈”的机制用于跟踪嵌套调用。当你调用一个方法或函数时,当前的“栈帧”(当你调用的方法返回时控制将转移到的地址,以及你方法中的任何参数和方法局部变量)被压入堆栈。当控制权返回到您的方法时,此堆栈帧会弹出,CPU 寄存器会恢复到之前的状态,以便您的方法可以继续执行。

          堆栈是一个固定的内存分配,因此您只能调用这么多级别的深度,然后才能用完存储恢复 CPU 寄存器状态所需的信息的位置当您的函数返回时。此时,发生堆栈溢出错误,因为您溢出了堆栈。您进行了如此多的嵌套调用,以至于内存不足,无法记录它们。

          这就是发生异常的原因。您无限递归:您的属性 getter 一遍又一遍地调用自己,直到堆栈上没有更多空间。

          【讨论】:

            【解决方案6】:

            因为您在 TotalCount getter 中引用 TotalCount。在达到堆栈溢出之前会发生无限循环。

            【讨论】:

              【解决方案7】:

              在尝试获取TotalCount 的值时,您正在循环往复

              大致上,获取 TotalCount 的逻辑是:

              • 获取 UnassignedCount 的值
              • 获取 Totalcount 的值

              重复、冲洗和清洗。

              编辑:至于为什么,看看Wiki

              【讨论】:

                【解决方案8】:

                你在一个无限递归中,一个属性在调用自己

                【讨论】:

                  【解决方案9】:

                  因为调用堆栈(理论上)会变得无限深。而且您没有无限的堆栈来保存您需要的所有数据。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-07-20
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-06-21
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多