C# 扫雷机中的递归函数给出堆栈溢出错误

C# 扫雷机中的递归函数给出堆栈溢出错误,c#,recursion,stack-overflow,C#,Recursion,Stack Overflow,好的,首先是按钮28x28正方形。当我使用递归函数禁用其中没有文本的所有按钮(button.Name=string.Empty)时,例如,当我在图像中单击8x2按钮时,递归函数禁用循环中从8x2到8x8向东的所有按钮。(代码1) 但当我向LoopControlEmpty函数添加更多递归方式时,会以不同的方式禁用更多块,如N、NW、SE等。。程序给了我一个堆栈溢出错误。(代码2) 我需要找到一种方法来调用按钮的名称,而不是在foreach循环中调用所有81个按钮 但它可能会再次给我同样的错误,因为

好的,首先是按钮28x28正方形。当我使用递归函数禁用其中没有文本的所有按钮(
button.Name=string.Empty
)时,例如,当我在图像中单击8x2按钮时,递归函数禁用循环中从8x2到8x8向东的所有按钮。(代码1) 但当我向
LoopControlEmpty
函数添加更多递归方式时,会以不同的方式禁用更多块,如N、NW、SE等。。程序给了我一个堆栈溢出错误。(代码2)

我需要找到一种方法来调用按钮的名称,而不是在foreach循环中调用所有81个按钮 但它可能会再次给我同样的错误,因为很多时候它会去递归。或者我需要找到另一种算法来禁用按钮

private void btn_Click(object sender, EventArgs e)
{
    var btn = sender as MineButton;
    int row = btn.Row;
    int col = btn.Col;

    ...
}
按钮名称与0x0按钮中的按钮类似=>button.name=“button0x0”

代码1

private void btn_Click(object sender, EventArgs e)
        {
            // Using sender as Button because i need to reach that buttons properties.
            Button btn = sender as Button;
            if (btn.Text == "M")
            {
                btn.BackColor = Color.FromArgb(255, 0, 0);
                foreach (Button button in this.Controls)
                {
                    button.Enabled = false;
                }
            }
            else if (btn.Text == string.Empty)
            {
                btn.Enabled = false;
                LoopControlEmpty(btn);
            }
            else
                btn.Enabled = false;
        }

        public void LoopControlEmpty(Button btn)
        {
            foreach (Button btnIn in this.Controls)
            {
                // Going East
                if (btn.Location.X + 28 == btnIn.Location.X && btn.Location.Y == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
            }
            return;
        }
代码2

public void LoopControlEmpty(Button btn)
        {
            foreach (Button btnIn in this.Controls)
            {
                // Going East
                if (btn.Location.X + 28 == btnIn.Location.X && btn.Location.Y == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
                //Going North
                if (btn.Location.X == btnIn.Location.X && btn.Location.Y + 28 == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
                //Goint South-West
                if (btn.Location.X - 28 == btnIn.Location.X && btn.Location.Y -28 == btnIn.Location.Y && btnIn.Text == string.Empty)
                {
                    btnIn.Enabled = false;
                    LoopControlEmpty(btnIn);
                    continue;
                }
                
            }
            return;
        }

原因是如果你向东,然后向西南,然后向北,你就到达了起点。这将永远持续下去

您需要跟踪哪些按钮已被处理。例如,这可以通过列表完成:将所有81个按钮放入列表中,然后从列表中删除已处理的按钮

public IList<Button> GetAllButtons()
{
    var buttonsToProcess = new List<Button>();
    foreach (Button btnIn in this.Controls)
    {
        buttonsToProcess.Add(btnIn);
    }
    return buttonsToProcess;
}

public void LoopControlEmpty(Button btn, IList<Button> buttonsToProcess)
{
    var neighbors = new List<Button>();
    foreach (Button btnIn in buttonsToProcess)
    {
        if (btnIn.Text != string.Empty) continue;
        // Going East
        if (btn.Location.X + 28 == btnIn.Location.X && btn.Location.Y == btnIn.Location.Y)
        {
            neighbors.Add(btnIn);
        }
        //Going North
        if (btn.Location.X == btnIn.Location.X && btn.Location.Y + 28 == btnIn.Location.Y)
        {
            neighbors.Add(btnIn);
        }
        //Goint South-West
        if (btn.Location.X - 28 == btnIn.Location.X && btn.Location.Y -28 == btnIn.Location.Y)
        {
            neighbors.Add(btnIn);
        }
    }
    // Make sure to not process them again
    foreach(Button neighbor in neighbors) 
    {
        buttonsToProcess.Remove(neighbor);
    }
    // Process the neighbors
    foreach(Button neighbor in neighbors) 
    {
        neighbor.Enabled = false;
        LoopControlEmpty(neighbor, buttonsToProcess);
    }
}
public IList GetAllButtons()
{
var buttonsToProcess=新列表();
foreach(此.Controls中的按钮btnIn)
{
按钮操作流程添加(btnIn);
}
返回按钮操作流程;
}
公共void LoopControlEmpty(按钮btn、IList按钮STOPROCESS)
{
var邻居=新列表();
foreach(按钮进程中的按钮btnIn)
{
如果(btnIn.Text!=string.Empty)继续;
//向东
if(btn.Location.X+28==btnIn.Location.X&&btn.Location.Y==btnIn.Location.Y)
{
添加(btnIn);
}
//北上
if(btn.Location.X==btnIn.Location.X&&btn.Location.Y+28==btnIn.Location.Y)
{
添加(btnIn);
}
//向西南移动
if(btn.Location.X-28==btnIn.Location.X&&btn.Location.Y-28==btnIn.Location.Y)
{
添加(btnIn);
}
}
//确保不要再次处理它们
foreach(按钮邻居中的邻居)
{
按钮操作过程。移除(邻居);
}
//处理邻居
foreach(按钮邻居中的邻居)
{
neighbor.Enabled=false;
LoopControlEmpty(邻居、按钮进程);
}
}

这不是一个好方法。在按钮矩阵中创建自己的具有逻辑坐标属性的按钮

public class MineButton : Button
{
    public int Row { get; set; }
    public int Column { get; set; }
}
还可以将其添加为枚举类型的状态属性

然后以编程方式创建按钮并将其添加到二维数组(矩阵)

以构造函数的形式

InitializeComponent();
for (int row = 0; row < FieldWidth; x++) {
    for (int col = 0; col < FieldHeight; col++) {
        var btn = new MineButton{
            Row = row,
            Column = col,
            Location = new Point(10 + 28 * col, 10 + 28 * row),
            Size = new Size(24, 24)
        }
        Field[col, row] = btn;
        Controls.Add(btn);
    }
}
由于现在您有了坐标,您可以很容易地在
字段
矩阵中找到相邻的按钮

if (col < FieldWidth - 1) {
    var buttonToTheRight = Field[col + 1, row];
    ...
}

如果按钮有一个status属性,那么您也可以根据其状态来做出决定。

我不明白为什么要递归。你能不能从这个函数返回当前按钮,并一直传递到
Button.Text==”
这听起来是学习如何使用调试器逐步调试代码并查看发生了什么的好时机。@RufusL不是*ss我知道那里发生了什么:)不,我是认真的。如果你一步一步地看代码,你应该能够看到为什么会有堆栈溢出。@RufusL它是溢出的,因为发生了很多递归,每次它在81个按钮中旋转不,3个只是示例,我不打算只做SW。想象一下,所有的方法都存在,但有3种方法已经开始出现堆栈溢出错误。@LesBenjamim:当然,更多的方法会使情况变得更糟。@LesBenjamim:我已经用一个建议更新了答案Kay我没有看到你的答案,所以这可能会解决我的问题,但我不知道我按下按钮多少次堆栈创建了一个错误,这就是为什么这种方法可以大大减少数量,但仍然可能会出现大量递归,因为当我按下8x2时,函数需要禁用除第一张图中的0x5之外的所有按钮,我确实解决了这个问题。非常感谢你!!是的,当我遇到错误时,我认为这是一种方法,但我试图找到一种方法,用它们的名称来访问按钮,这就是为什么我给了它们button.Name=$“button{i}x{j}”,而不是从头开始。是否有任何方法可以通过使用Name属性来处理此问题,因为当我按3x3时,如果我只能达到2x2 2x3 2x4 3x2 3x4 4x2 4x4 4x4 4x4 4x4,则只有这些按钮可能会解决此问题?您还可以添加一个不可见按钮的边框,这些按钮只会添加到2d数组中,而不会添加到窗体中。
字段
数组将更大、更高两个按钮。这将大大缓解边界条件,因为您可以始终按3x3操作。
if (col < FieldWidth - 1) {
    var buttonToTheRight = Field[col + 1, row];
    ...
}
if (btn.Enabled) {
    btn.Enabled = false;
    do recursive calls, etc.
}