【问题标题】:.Net: Try...Catch paranoia - where does it end?.Net:尝试...抓住偏执狂——它在哪里结束?
【发布时间】:2011-05-01 23:02:25
【问题描述】:

我想我有“程序员的强迫症”之类的东西。我喜欢我的代码既美观又干净,我希望它是“完美的”(就像正确而漂亮地处理所有可能的情况一样)。我经常发现自己花费大量时间一遍又一遍地检查相同的区域,以查看可以优化的地方以及可以防呆的地方。

所以当涉及到 try...catch 块时,我对要封装什么感到有点偏执。我的意思是,我在哪里画出代码应该满足的界限?以文件处理为例。我是否应该将 每个 该死的文件操作放在 try...catch 块中,以防 某事 可能发生(文件被外部的某人/某事锁定)应用程序、磁盘损坏等)?

有时它会让我感觉也许某事(我什至不知道)可能会导致某些代码出错。..

编辑:

我不是在谈论使用 try...catch 来掩盖糟糕的编程,我说的是在其他方面可以正确实施但依赖于我无法控制的其他因素的操作和过程 - 即使它们可能是晦涩难懂的(这就是重点),并且只会在我没有预见到的极其“不幸”的情况下发生。

文件处理就是一个明显的例子。当我容易感到不安时,就是在想知道在其他内置功能的幕后进行了什么样的处理,以及它如何响应我的代码。

这是一个例子:

Dim serverUrl as String = My.Settings.ServerUrl

那里涉及磁盘操作(从 app.config 读取)。 that 是否应该包含在 try...catch 块中?这就是我的意思是它在哪里结束。

担心内存泄漏是另一回事。只有非托管代码会在那里构成威胁吗?我怎么知道什么是非托管代码?有清单吗?

更多编辑:

另一个我没有信心的领域是当幕后某处存在访问限制或政策时。

当我阅读有关编程的文章和讨论时,我看到很多解释类似于“嗯,您的问题是,当您调用 X 时,.Net 在内部尝试访问某某,除非您的应用程序在上下文类型 Y 中运行,或者您拥有权限 Z,否则它将引发异常”。这只是增加了我的偏执 - 在构建防水异常处理方面。因为我只是不了解该语言/平台的所有内部工作原理,也不知道该去哪里寻找(无需毕生致力于研究它)。

我希望特别是能有某种形式的纲要或简明的wiki,以概述需要特别注意的编程领域(文件处理等),带有示例场景、典型挑战和罪魁祸首(带有解决方案)、最佳实践模型、编程模式,并且最重要的是,为像我这样的凡人提供了一组指南,不幸的是没有参与实际构建语言及其库。

所有这些都在一个地方,而不必在语言参考或网络上的随机文章中查找零散的信息-我什至不知道要查找什么,在许多案例。

至于我当前的特定项目,它位于 Windows 服务的上下文中。没有 UI,我正在处理的子任务之一是创建一个健壮的引导程序,可以优雅地处理所有问题场景。在这种情况下,一切都是关于日志记录——然后要么忽略异常(如果它足够微不足道的话)——或者干脆退出!如果问题发生在尝试登录时 - 那我该怎么办?只是退出 - 不知道发生了什么?这个引导程序只记录它的启动(之后,动态加载的主程序集接管并记录它自己的东西,尽管面临相同的挑战),并将其写入一个简单的“bootstrap.log”文件。将它记录到 EventLog 中会更好(或有价值的补充)吗?或者,EventLog 是否是另一个可能引发新问题的领域(再次,访问限制等。EventLog 是否基于需要“尝试并捕获”的磁盘操作..? )

看到了吗?偏执狂。

【问题讨论】:

  • 嗯。这是一个非常有趣的软件工程问题,但没有人回应。我怀疑这是因为[vb] 标签。请原谅我重新标记您的问题,以使其对更广泛的受众感兴趣。
  • @Konrad - 你被原谅了 :) 谢谢。

标签: .net exception-handling try-catch


【解决方案1】:

我是否应该将每个该死的文件操作放在一个 try...catch 块中,以防可能发生某些事情(文件被应用程序外部的某人/某事锁定、磁盘损坏等)?

一般来说是的。

必须处理的异常

文件操作本质上是不安全的,地狱当你使用它时可能会崩溃。假设您正在读取存储在 USB 存储设备上的文件,并且在读取过程中用户拔出 USB 记忆棒。

这会引发异常,您需要提防这种情况。在您提到的特定示例中也是如此。

另一方面,通常只有高级代码需要处理这种错误,而不是业务逻辑深处的代码。 通常可以让方法失败并作为 IO 失败的结果传播异常。 重要的是防止此异常导致应用程序崩溃,并通知用户出现问题,或重试,或采取规避措施。

哪些层处理异常?

An article from 2003 by Ned Batchelder 解释了代码的哪些部分应该防止异常,哪些不应该:

他将代码分为三层:

  • A 层,a适配其下方的 API,
  • B 层,b构建系统的各个部分,以及
  • C 层,c将它们组合在一起。

我会选择稍微不同的层:

  • 低级层(由 A 层和 B 层组成)构建系统的低级 API 并适应更低级别的 API),
  • 实现大部分业务逻辑的中间层(主要是 B 层和 C 层)和
  • 用户与之交互的UI 层

无论如何,最低层应异常从较低级别的 API 转换到您的域中。

中间层应该让异常通过:业务层没有异常处理。这也应该是你的大部分代码,这意味着你不需要在大部分代码中处理异常(但当然也可能有这条规则的例外)。

并且上层应该捕捉异常并响应它们,或者以可读的格式呈现给用户。 处理此类异常的一个好方法是安装全局异常处理程序,或者将 GUI 应用程序中的工作卸载到额外线程(例如 BackgroundWorker)并在线程终止时捕获异常。

不处理的异常

并非所有异常都需要以这种方式处理。例如,您不应该尝试处理StackOverflowException(您不能!)或OutOfMemoryException(您通常不能)。

此类异常意味着代码或机器存在严重错误,您的应用程序没有机会从故障中恢复。

【讨论】:

  • 我在问题编辑中添加的示例怎么样:访问应用程序设置..?
  • 在我当前的项目中,我正在开发一个没有 UI 的 Windows 服务,所以这都是关于日志记录的。但是日志记录函数本身遇到异常在尝试记录时(例如写入日志文件),它们无法真正记录它。所以我有一种不完整的感觉。在 my 应用程序的上下文中,还有一组“层”:首先是一个引导程序,它记录其启动成功(仅记录到日志文件 - 这是非常基本的)并扫描最符合条件的“引擎”插件。引擎是下一层,它有一个更冗余的系统。
  • 它将发送到事件日志、磁盘日志、内存日志(可以通过联系内置 WCF 服务进行远程轮询)、数据库,并通过电子邮件/短信,安排(在代码,不在我的列表中),按照它们有多少失败的可能性或它们对其他组件的依赖程度(这可能会阻止它们执行)来排序。这意味着它有几个“安全网”,至少可以向管理员获取一些信息。尽管如此,在最低级别,似乎只有两个选择:要么在失败后“跳过”日志记录(这很危险,因为与 [cont]
  • 导致日志记录尝试失败很容易成为应用程序本身实际运行的阻碍 - 然后没有任何日志记录),或者只是退出应用程序(但没有办法让外界知道发生了什么)。
  • [第一条评论应该说“但是如果日志记录函数本身遇到异常......” - 我刚刚意识到标签格式在这些 cmets 中不起作用:|]
猜你喜欢
  • 2021-04-04
  • 1970-01-01
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多