正如标题所述,如何在同一方法中重新运行方法而不会出现名为StackOverflowException的错误未处理? (我把星号放在问题的地方。“Random rnd = new Random();”然后以绿色突出显示错误。)
private void AI()
{
Random rnd = new Random();
int aimove = rnd.Next(0, 7);
CheckIfValid();
if (aimove == 0 && cc0 == false || aimove == 1 && cc1 == false || aimove == 2 && cc2 == false || aimove == 3 && cc3 == false ||
aimove == 4 && cc4 == false || aimove == 5 && cc5 == false || aimove == 6 && cc6 == false)
{
**AI();**
}
else if (aimove == 0 && cc0 == true || aimove == 1 && cc1 == true || aimove == 2 && cc2 == true || aimove == 3 && cc3 == true ||
aimove == 4 && cc4 == true || aimove == 5 && cc5 == true || aimove == 6 && cc6 == true)
{
if (aimove == 0)
{
c0++;
Drop(0);
}
else if (aimove == 1)
{
c1++;
Drop(1);
}
else if (aimove == 2)
{
c2++;
Drop(2);
}
else if (aimove == 3)
{
c3++;
Drop(3);
}
else if (aimove == 4)
{
c4++;
Drop(4);
}
else if (aimove == 5)
{
c5++;
Drop(5);
}
else if (aimove == 6)
{
c6++;
Drop(6);
}
}
}
答案 0 :(得分:1)
如果AI()再次调用自身,那么与aimove值相关的ccX bool目前是假的。
机会是cc1 - cc6都设置为false,因此AI()将始终调用自身,创建Random的新实例,直到出现堆栈溢出。
您需要添加一个退出条件,将cc1-cc6布尔值设置为true,以便递归链结束。
答案 1 :(得分:0)
我将从这里开始:简化代码,以便更容易推理。
我们可以做一些事情。
从if
语句开始,我们可以这样做:
var ccs = new [] { cc0, cc1, cc2, cc3, cc4, cc5, cc6 };
if (ccs[aimove] == false)
{
AI();
}
else if
条件只是第一个条件的逻辑否定,因此我们只能用else
替换。
else中的每个if
都做了很多相同的事情。我们可以使用字典来简化它。
所以代码可以成为:
private void AI()
{
Random rnd = new Random();
int aimove = rnd.Next(0, 7);
CheckIfValid();
var ccs = new [] { cc0, cc1, cc2, cc3, cc4, cc5, cc6 };
var increments = new Dictionary<int, Action>()
{
{ 0, () => c0++ },
{ 1, () => c1++ },
{ 2, () => c2++ },
{ 3, () => c3++ },
{ 4, () => c4++ },
{ 5, () => c5++ },
{ 6, () => c6++ },
};
if (ccs[aimove] == false)
{
AI();
}
else
{
increments[aimove]();
Drop(aimove);
}
}
现在,这仍然有点凌乱。将cc0
.. cc6
和c0
... c6
重新定义为数组会很高兴,如下所示:
private int[] cs = new int[7];
private bool[] ccs = new bool[7];
然后,您可以像这样重写代码:
private void AI()
{
Random rnd = new Random();
int aimove = rnd.Next(0, 7);
CheckIfValid();
if (ccs[aimove] == false)
{
AI();
}
else
{
cs[aimove]++;
Drop(aimove);
}
}
现在问题可能来自所有ccs
值都是假的事实,无论aimove
的值是什么,代码都会无限循环。
所以,让我们检查一下。
private void AI()
{
Random rnd = new Random();
int aimove = rnd.Next(0, 7);
CheckIfValid();
if (ccs.All(cc => cc == false))
{
throw new System.InvalidOperationException("All `ccs` are false");
}
if (ccs[aimove] == false)
{
AI();
}
else
{
cs[aimove]++;
Drop(aimove);
}
}
现在,如果所有ccs
都为假,那么异常将触发,您知道您的代码将会破坏堆栈。
另一个问题也会让你感到悲伤,即使某些ccs
成立,也可能会导致堆栈崩溃,因为你要为每个问题创建一个新的Random
实例致电AI
。这个问题是它将在大多数调用中使用相同的种子进行实例化,因为调用是如此迅速。如果它是相同的种子,则对rnd.Next(0, 7)
的调用将出现相同的值。如果对AI
的每次递归调用都具有相同的aimove
值,那么这也可能会快速炸毁堆栈。
诀窍是只创建Random
的一个实例,然后重复使用。
所以将代码更改为:
private Random rnd = new Random();
private void AI()
{
int aimove = rnd.Next(0, 7);
CheckIfValid();
if (ccs.All(cc => cc == false))
{
throw new System.InvalidOperationException("All `ccs` are false");
}
if (ccs[aimove] == false)
{
AI();
}
else
{
cs[aimove]++;
Drop(aimove);
}
}
请告诉我这是否可以解决问题。
我怀疑,你真正要做的是选择一个有效的aimove
,如果你选择了一个无效的,你正在递归调用AI
来选择一个不同的之一。
请改为尝试:
private void AI()
{
if (ccs.All(cc => cc == false))
{
throw new System.InvalidOperationException("All `ccs` are false");
}
int aimove = -1;
do
{
aimove = rnd.Next(0, 7);
CheckIfValid();
} while (ccs[aimove] == false);
cs[aimove]++;
Drop(aimove);
}
这里它只是在AI
方法中循环,直到它选择有效的移动。
另一个最终选择是直接使用LINQ选择有效的移动。
private void AI()
{
var aimove =
ccs
.Select((cc, n) => new { cc, n })
.Where(x => x.cc)
.OrderBy(x => rnd.Next())
.Select(x => x.n)
.First();
cs[aimove]++;
Drop(aimove);
}
如果没有任何有效的移动,.First()
将抛出异常。我不知道CheckIfValid();
正在做什么,所以我不知道把它放在哪里。