为什么C#没有轻量级异常?

Jez*_*Jez 5 .net c# error-handling exception

它经常说,你不应该使用异常定期错误处理,因为糟糕的表现。我的猜测是,性能差是由于必须实例化新的异常对象,生成堆栈跟踪等导致的。那么为什么不具有轻量级异常呢?这样的代码在逻辑上是合理的:

string ageDescription = "Five years old";
try {
    int age = int.Parse(ageDescription);
}
catch (Exception) {
    // Couldn't parse age; handle parse failure
}
Run Code Online (Sandbox Code Playgroud)

但是我们还是建议使用它TryParse来避免异常的开销。但是,如果异常只是在线程启动时初始化的静态对象,则抛出异常所需的所有代码都将设置一个错误代码号,甚至可能是一个错误字符串。没有堆栈跟踪,没有新的对象实例。这将是“轻量级异常”,因此使用异常的开销将大大减少。为什么我们没有这么轻量级的例外?

Hon*_*tan 5

异常对象实例化是整个案例中最小的问题。真正的性能杀手是控制流必须停止执行程序,并且必须在调用堆栈中查找可以捕获抛出的异常的可能处理程序(catch 块),然后必须执行正确的处理程序(及其finally 块) ,在被告知时重新抛出异常,然后在正确的位置继续执行程序,即在最后一个处理程序之后。您的“轻量级”异常的想法不会改变这一点,它甚至会减慢线程的创建速度,因为它必须创建和存储异常对象,并且会阻止按类型过滤异常,这现在是可能的。

通过使用 TryParse,您可以通过一个简单的条件子句来避免所有这些,而且您实际上编写的代码更少,并且更容易阅读和推理。

例外是针对特殊情况,在这种情况下,它们为日志/调试器提供了大量有用的信息。


Dav*_*kle 4

性能下降不仅仅是因为您创建了一个新的 Exception 对象。其中很大一部分与异常发生时需要执行的堆栈的条件展开有关。

例如,考虑一下当您有捕获不同类型异常的异常处理程序时必须完成的工作。在堆栈中的每个点,当它从被调用者展开到调用者时,语言必须执行类型检查,不仅要查看是否可以处理异常,还要查看最合适的处理程序是什么。这本身就是一笔巨大的开销。

如果你真的想变得轻量级,你应该从你的函数返回一个结果——这就是 Int32.TryParse() 所做的。没有堆栈展开,没有类型检查,只是一个可以轻松优化的简单条件。

编辑:值得注意的一件有趣的事情是 C# 是在 Java 之后创建的。Java 有几个有趣的结构,它们导致异常处理比我们在 C# 中看到的更复杂,即检查异常和throws关键字。有点有趣的读物。我(个人)很高兴 C# 没有包含这个“功能”。我的猜测是,他们将异常处理程序分开以提高性能。据我了解,在现实世界中,很多开发人员最终只是throws exception在他们的函数声明中进行指定。

  • 就像我在评论 Honza 的出色答案时所说的那样,C# 语言设计者可以做很多事情。为什么某些东西不在语言中的核心答案,我怀疑你的问题的答案是,“虽然有可能,但当有另一种更简单的方法来实现相同的结果时,不一致是不值得的(高表现)”。 (4认同)
  • @user3339997:是的,确实是这个意思。在 C# 代码中通常应避免返回错误代码的模式。 (2认同)