【问题标题】:Why does this button object cause a stack overflow? [duplicate]为什么这个按钮对象会导致堆栈溢出? [复制]
【发布时间】:2021-05-30 15:14:55
【问题描述】:
 void OpenClean( int i, int j) {

        bnt[i, j] = new Button();
        if (!bnt[i,j].Enabled) return;

        bnt[i, j].Enabled = false;
        bnt[i,j].BackColor = Color.Aquamarine;
        bnt[i, j].Text = data[i, j].ToString();
        if (data[i, j] == 0) {

            if (i > 0 && j > 0 && data[i - 1, j - 1] == 0) OpenClean(i - 1, j - 1);
            if (i > 0 && data[i - 1, j] == 0) OpenClean(i - 1, j);
            if (i > 0 && j < 8 && data[i - 1, j + 1] == 0) OpenClean(i - 1, j + 1);
            if (j > 0 && data[i, j - 1] == 0) OpenClean(i, j - 1);
            if (j < 8 && data[i, j + 1] == 0) OpenClean(i, j + 1);
            if (i < 8 && j > 0 && data[i + 1, j - 1] == 0) OpenClean(i + 1, j - 1);
            if (i < 8 && data[i + 1, j] == 0) OpenClean(i + 1, j);
            if (i < 8 && j < 8 && data[i + 1, j + 1] == 0) OpenClean(i + 1, j + 1);
        }

它告诉我进程因 StackOverflowException 而终止。 由于我的经验有限,我没有找到有效的方法。 这个问题困扰了我很久。 请帮助或尝试给我一些解决方法的想法。

提前致谢!

【问题讨论】:

  • 您正在反复调用您的 OpenClean 方法。我没有检查您的所有条件,(而且我无法检查,因为我不知道 data 数组的内容)但您的条件可能有错误,因此您的递归调用永远不会停止。一段时间后会导致 StackOverflow 异常
  • 这是学习如何使用调试器的好时机。在方法上设置断点。当程序停止时,查看调用堆栈,记下参数是什么。一步,一步,一步。继续前进,直到您再次调用该方法(然后您将再次遇到断点)。发生的事情应该很明显

标签: c#


【解决方案1】:

您永远不会将data[i, j] 设置为0 以外的值。因此,递归永远不会停止。您必须将检查的单元格设置为不同的值,这样它们就不会被一次又一次地检查...

您还可以使用bnt[i, j] = new Button(); 创建一个新按钮,而不是检查现有按钮。因此,您将永远不会看到带有Enabled == false 的按钮,因为default value for Enabled is true。

最好将逻辑基于模型(即data[,])并仅使用 UI(控件)进行显示。

const int Inspected = -1;
void OpenClean( int i, int j) {
    ...
    if (data[i, j] == 0) {
        data[i, j] = Inspected; // <====== This stops the endless recursion!
        if (i > 0 && j > 0 && data[i - 1, j - 1] == 0) OpenClean(i - 1, j - 1);
        ...
    }
}

而且,顺便说一句,您正在测试0 两次。在再次调用OpenClean 和内部OpenClean 之前。只做一次。

void OpenClean( int i, int j) {
    if (data[i, j] == 0) { // This one does the job.
        data[i, j] = Inspected; // <======
        // bnt[i, j] = new Button();  <=== drop this!
        // if (!bnt[i,j].Enabled) return;  <=== drop this! We test data instead.

        bnt[i, j].Enabled = false;
        bnt[i,j].BackColor = Color.Aquamarine;
        bnt[i, j].Text = data[i, j].ToString();

        if (i > 0 && j > 0) OpenClean(i - 1, j - 1);
        if (i > 0         ) OpenClean(i - 1, j);
        if (i > 0 && j < 8) OpenClean(i - 1, j + 1);
        if (         j > 0) OpenClean(i,     j - 1);
        if (         j < 8) OpenClean(i,     j + 1);
        if (i < 8 && j > 0) OpenClean(i + 1, j - 1);
        if (i < 8         ) OpenClean(i + 1, j);
        if (i < 8 && j < 8) OpenClean(i + 1, j + 1);
    }
}

如果您在OpenClean 开头进行范围测试,则可以摆脱所有其他范围测试。

void OpenClean( int i, int j) {
    if (i > 0 && i < 8 && j > 0 && j < 8 && data[i, j] == 0) { // This one does the job.
        data[i, j] = Inspected; // <======

        bnt[i, j].Enabled = false;
        bnt[i,j].BackColor = Color.Aquamarine;
        bnt[i, j].Text = data[i, j].ToString();

        OpenClean(i - 1, j - 1);
        OpenClean(i - 1, j);
        OpenClean(i - 1, j + 1);
        OpenClean(i,     j - 1);
        OpenClean(i,     j + 1);
        OpenClean(i + 1, j - 1);
        OpenClean(i + 1, j);
        OpenClean(i + 1, j + 1);
    }
}

使用 C# 9.0 模式,您可以稍微简化条件

if (i is > 0 and < 8 && j is > 0 and < 8 && data[i, j] == 0)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-20
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 2016-02-24
    • 1970-01-01
    相关资源
    最近更新 更多