C# 中的 Try-Catch 与 If 性能[重复]

问题描述 投票:0回答:1

我试图更多地了解各种设计选择的含义,并找到最有效的解决方案来防止代码中的错误。在深入探讨之前,请允许我简要概括地陈述我的问题:

与 if 语句结构相比,try catch 块是处理越界异常的更有效解决方案吗?

我知道引发异常的成本很高,但是通过删除不必要的 if 语句可以减轻这种成本吗?

现在,请允许我陈述我的具体问题,以便为您提供更多有用的信息。

我正在构建一个游戏,该游戏的一部分是一个世界网格解决方案以及一个探路者。游戏单元可以从世界网格请求节点(通过以有序对 (x, z) 的形式发送坐标信息),然后发送到探路者或对它们执行一些其他杂项操作。因为节点的请求频率很高,尤其是探路者请求的频率,所以这似乎是一个尝试优化的明智地方。

这是我当前的代码,用于根据节点数组中的 xz 值返回给定节点:

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 块来确保我的代码运行时不会出现致命错误,除非进一步测试表明有其他情况。

感谢所有做出贡献的人。

c# if-statement try-catch
1个回答
12
投票

不会通过异常来控制代码的流程。它们是为了治疗“特殊”情况,例如连接问题或无效的文件格式。

如果您想控制代码流程,您应该使用条件语句,例如

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 个刻度

我想您可以看到并且特别感受到其中的差异。

© www.soinside.com 2019 - 2024. All rights reserved.