为什么不";尝试";方法无处不在

本文关键字:quot 无处不在 方法 尝试 为什么不 | 更新日期: 2023-09-27 18:27:56

我想知道,为什么微软开发人员不在任何地方为我们提供不可靠方法的"Try"版本?

如果我使用数据库连接或smtp客户端,我总是必须考虑例外情况:

try
{
    smtpClient.Send(message);
}
catch (SmtpException)
{
    // test with real smtp server and analyze an exception and it's contents here
}

但作为一名用户,我想要的是:

if(!smtpClient.TrySend(message, out reason))
{
    // analyze reason here
}

对我来说,最大的问题是异常性能开销。我就是负担不起对我的服务的每个客户端调用都抛出异常。如果我需要检查几十个连接/提供程序,这可能会导致每个客户端请求出现几十个异常。

我的问题是:.NET中这样的API设计决策背后的基本原理是什么?我不认为这是一个错误,因为它无处不在,而不是在一项技术中。

更新我必须再举一个.NET中异常的例子来停止谈论网络延迟。例如,如果windows服务停止,System.Diagnostics.EventLog.WriteEntry方法可以抛出System.ComponentModel.Win32Exception

为什么不";尝试";方法无处不在

我想知道,为什么微软的开发人员不到处为我们提供不可靠方法的"尝试"版本?

由于我不是微软的员工,我只能猜测。我的猜测是,这有两个原因:

  • 他们在设计.NET类库的第一个版本时没有考虑到这一点——注意,TryParse方法已在以后的版本中添加。

  • 性能差异不够重要(见下文),无法保证实现和维护新的Try...方法的成本。.NET框架还需要其他更迫切的东西。

对我来说,最大的问题是异常性能开销。

我非常怀疑这一点。SMTP超时(=几秒)的"性能开销"占引发异常(不到一毫秒)的"绩效开销"的1000多倍。

考虑到您正在调用基于网络的服务,抛出异常本身的成本是而不是导致性能下降的原因。找出何时抛出异常的成本才是罪魁祸首:系统需要尝试网络调用,等待它超时或返回故障,然后才抛出异常。

实际投掷的成本是最低的,因此提供Try方法并不能帮助您提高性能。然而,它可能会帮助您提高可读性,这不是一件小事。为此,您可以编写自己的Try包装器/扩展方法,并在其中隐藏异常捕获:

public static bool TrySend(this SmtpClient client, object message, out Error reason) {
    try {
        client.Send(message);
        reason = null;
        return true;
    } catch (SmtpException ex) {
        reason = ex.Reason;
        return false;
    }
}

好的,您还没有看到异常的意义。你的建议(所有东西都是Try…版本)基本上完全否定了例外的观点。异常的美妙之处在于,您不需要检查每个方法调用的结果。您只需假设一切都会成功(常规情况),如果一个调用失败,它将被"捕获"并在调用堆栈中的预定位置进行处理。您的代码将变得更加可读,因为它只有重要的步骤,而没有管道。尝试对于更可能/更常见"失败"的场景来说是很好的,它们并不是真正的错误或问题,而是正常流控制逻辑的一部分。

异常是OO方式,返回错误代码是老派。考虑一个深度嵌套的调用层次结构,以及在所有级别处理错误代码并适当处理它们的责任。将其与使用异常进行比较。你看到光了吗?:-)。

与正在执行的发送/数据库操作相比,异常性能开销可以忽略不计。因此,有些场景(像这样)在某种程度上不需要支持TryParse模式。

如果你的场景中有"几十个连接/提供程序"导致"几十个异常",那么可能值得检查一下为什么会发生这种情况,这可能是你的smtp服务器的全局问题。

p.S.披露我是微软的员工,但即使我在苹果或谷歌工作,我也会同样回答你:)

您不经常看到Try方法的主要原因是异常传达了一些信息。出现"异常"情况,必须由处理。您刚刚执行的操作非常重要,失败会导致整个过程关闭。抛出异常的方法无法处理这种情况,而抛出异常会强制某些代码块处理这种情况。

在大多数情况下,网络调用对应用程序的成功运行至关重要,因此例外情况是适当的。此外,异常包含一条消息和堆栈跟踪,这在调试故障时是必不可少的。您提出的解决方案会剥离关键的调试信息。这并不总是至关重要的,就像解析int一样,尤其是在通常很容易从错误中恢复的情况下。

一个失败的网络呼叫或写入光盘是完全不同的野兽。

这个问题当然是合理的。以Eric Lippert关于异常的博客文章为基础,考虑到你采取了必要的预防措施来确保发送失败是因为某种网络条件(而不是因为你在某个地方传递了无效的参数),我将SmtpException归类为外生类文件。打开,你可能无法阻止它。正因为如此,我不会把SmtpException归类为令人烦恼的,它不是糟糕的设计选择(比如int.Parse)的结果

确保SmtpException确实指示异常,并且异常将是一个合适的工具。正如其他人所说,开销可以忽略不计。