我试图更多地了解各种设计选择的含义,并找到最有效的解决方案来防止代码中的错误。在深入探讨之前,请允许我简要概括地陈述我的问题:
与 if 语句结构相比,try catch 块是处理越界异常的更有效解决方案吗?
我知道引发异常的成本很高,但是通过删除不必要的 if 语句可以减轻这种成本吗?
现在,请允许我陈述我的具体问题,以便为您提供更多有用的信息。
我正在构建一个游戏,该游戏的一部分是一个世界网格解决方案以及一个探路者。游戏单元可以从世界网格请求节点(通过以有序对 (x, z) 的形式发送坐标信息),然后发送到探路者或对它们执行一些其他杂项操作。因为节点的请求频率很高,尤其是探路者请求的频率,所以这似乎是一个尝试优化的明智地方。
这是我当前的代码,用于根据节点数组中的 x 和 z 值返回给定节点:
public Node GetTile(int x, int z)
{
if (x < 0)
x = 0;
if (x >= length_X)
x = length_X - 1;
if (z < 0)
z = 0;
if (z >= length_Z)
z = length_Z - 1;
return tiles [x, z];
}
正如您可以清楚地看到的,我有几个 if 语句,以避免从世界网格的节点数组(图块)中检索节点时出现越界异常。这是最有效的解决方案吗?我可以删除 if 并将 return 放在 try catch 块中,如下所示:
public Node GetTile(int x, int z)
{
try
{
return tiles[x, z];
}
catch (System.IndexOutOfRangeException e)
{
//handle exception here
//do smart programming fix-y stuff
}
}
这个解决方案有效,但是是否有一些我不知道的原因导致其效率降低?我相信它是一个更智能的解决方案的逻辑是,每次调用该方法时,我都会删除最多四次比较。考虑到程序实际发送超出界限的值的情况很少见,这似乎是一个很好的权衡。但我不知道异常在幕后是如何工作的。检查异常是否与手动检查值一样昂贵?
更新:
读完这里的回复后,我现在对这个主题有了更好的了解。我对我的代码运行了一些测试,考虑了运行 1000 万次迭代的各种情况。
根据我的一般假设,如果我们假设永远不会出现输入导致异常的情况,那么 try catch 块确实更快。然而,在大约 10% 的方法调用引发异常的情况下,性能会慢 5 倍以上。我测试了各种百分比,在我的案例中,获得更好性能的阈值似乎接近 1%。
到目前为止,我不确定真正会引发异常的调用百分比是多少,但我相信这个数字将远低于 1%。因此,我可以安全地使用 try-catch 块来确保我的代码运行时不会出现致命错误,除非进一步测试表明有其他情况。
感谢所有做出贡献的人。
不会通过异常来控制代码的流程。它们是为了治疗“特殊”情况,例如连接问题或无效的文件格式。
如果您想控制代码流程,您应该使用条件语句,例如
if
、else
和 switch
。
与一些简单的
if
语句相比,异常的性能通常较差,但也存在异常处理可能比手动检查数据有效性更快的情况。
在很多情况下,您知道可能会引发异常,但您只是信任数据 - 例如检查您自己的设置文件的有效 xml 格式。数据验证按 CPU 时间付费。
大多数时候你想使用
Exceptions
以便稍后获得错误信息,例如xml 文件中错误的行号并以某种方式恢复您的应用程序(例如重新连接到套接字)。
你不能对异常的表现给出一般性的陈述,但是当你可以避免它们时,你应该这样做。 正如其名称所示,它们是为例外而设计的。
一个可以避免异常的小例子:
try
{
LoginUser("Fooooo", "Bar");
} catch(InvalidCredentialsException e) {
// handle
}
private static void LoginUser(string pUsername, string pPassword)
{
if (pUsername != "Foo" || pPassword != "Bar")
throw new InvalidCredentialsException("Invalid username and/or password");
GrantAccess(pUsername);
}
您也可以只返回一个布尔值,而不是抛出异常:
if (!LoginUser("Fooooo", "Bar"))
{
// handle
}
private static bool LoginUser(string pUsername, string pPassword)
{
if (pUsername != "Foo" || pPassword != "Bar")
return false;
GrantAccess(pUsername);
return true;
}
我的机器上使用无效凭据运行 10000 次尝试的性能:
扔
Exception
:181990454 滴答
返回
boolean
:644 个刻度
我想您可以看到并且特别感受到其中的差异。