在以下代码块中,new
构造函数被记录为抛出七种不同的异常类型,包括System.IO.PathTooLongException
和System.ArgumentException
,System.UnauthorizedAccessException
和System.SecurityException
:
try
{
var fileInfo = new FileInfo(path);
}
catch ???
我只是想确保path
实际上是一个可访问的路径,当我尝试使用此path
创建文件时,没有任何不妥。
所以,问题:
图书和编码指南告诉我我不应该使用catch (Exception)
构造,但是我面临以下情况 - 我可以捕获并处理4个(7个中)异常类型及其对每种异常类型的处理完全相同。
应该怎么做?
我也可以想到以下解决方案,但它看起来仍然很糟糕:
catch (Exception exception)
{
Debug.Assert(exception is PathTooLongException || exception is ...);
// (or maybe rethrow it further if there is a type mismatch)
//... Handling code ...
}
答案 0 :(得分:2)
您不必在每行代码中捕获每个可能的异常。
相反,只有当你有上下文并且你准备好处理并采取适当的行动时才会捕获。否则,就让它冒泡。
捕获异常(特别是在没有足够上下文处理的常见函数中的堆栈深处)可能会有问题。它有时会导致吞咽和不适当的处理。有上下文时处理异常。
对于该FileInfo异常,它取决于该堆栈的深度。如果它在一个可以通过多个路径和场景到达的库中,你就不应该处理,那时你打算做什么?如果情况也是如此,并且它通过深层堆栈一直冒出一个GUI来执行你想要处理它的操作,那么你会抓到什么?它的执行路径上的每个代码路径的每个可能的例外?而且,随着代码的变化,您将重新评估所有路径?在一个浅的代码路径(调用该方法的文件信息对话框)中,这是一个简单的调用,但即便如此,您也不会以不同的方式处理它 - 您将在面板中显示错误对话框或消息。异常类型提供了处理不同的能力。在这两种情况下,你处理异常,所以我说bah hum bug到那个指南。
非常清楚的是你不应该抛出类型Exception 。这会缩短处理能力。
另一篇相关文章:
答案 1 :(得分:2)
此外,您可以先使用File.Exists(路径)或Directory.Exists(路径)
测试路径答案 2 :(得分:1)
我想在此处添加,因为我所看到的几乎所有java代码中的异常处理都是错误的。即你最终会因为忽略异常而很难调试错误,或者同样糟糕的是,你会得到一个模糊的异常,它不会告诉你什么,因为盲目地跟着“捕获(异常)是坏的”而事情就更糟了。
首先,要了解异常是一种促进跨代码层返回错误信息的方法。现在,错误1:一个层不仅仅是一个堆栈帧,一个层是具有明确定义的责任的代码。如果你只是编码接口和impl只是因为,你有更好的东西要修复。
如果图层设计得很好并且具有特定的职责,那么错误的信息在起泡时具有不同的含义。 < -t这是做什么的关键,没有普遍的规则。
因此,这意味着当发生异常时,您有2个选项,但您需要了解图层中的位置:
A)如果你处于一个层的中间,而你只是一个内部的,通常是私有的帮助函数,那就会变坏:不要担心,让调用者接收异常。它非常好,因为你没有业务背景和 1)你没有忽略错误和 2)调用者是你的图层的一部分,应该知道这可能发生,但你现在可能不会在下面处理它。
或......
B)你是图层的顶部边界,是内部的立面。然后,如果你得到一个异常,那么默认值应该是CATCH ALL并且阻止任何特定的异常进入上层,这对调用者没有意义,或者更糟糕的是,你可能会改变,调用者将依赖于一个实现细节,两者都会破裂。
应用程序的强度是层之间的解耦级别。在这里,您将作为一般规则停止所有操作,并使用一般异常重新抛出错误,将信息转换为上层的更有意义的错误。
规则:层的所有入口点都应使用CATCH ALL保护,并且所有错误都会被翻译或处理。现在这个'handeled'只发生1%的时间,大多数情况下你只需要(或者可以)以正确的抽象方式返回错误。
不,我确定这很难理解。真实的例子 - >
我有一个包运行一些模拟。这些模拟是在文本脚本中。有一个包编译这些脚本,有一个通用的utils包只读取文本文件,当然还有基本的java RTL。 UML依赖性是 - >
Simulator-> Compiler-> utilsTextLoader-> Java文件
1)如果某个私有内的utils加载器出现问题,我会得到一个FileNotFound,Permissions或者什么,只是让它通过。你无能为力。
2)在边界处,在最初调用的utilsTextLoader函数中,您将遵循上述规则和CATCH_ALL。编译器不关心发生了什么,现在只需要加载文件是否已加载。因此,在catch中,重新抛出一个新的异常并将FileNotFound或其他任何内容转换为“无法读取文件XXXX”。
3)编译器现在知道未加载源。多数民众赞成需要知道的。因此,如果我稍后将utilsTestLoader更改为从网络加载,编译器将不会更改。如果你放弃FileNotFound并稍后更改,你将不会影响编译器。
4)循环重复:调用文件的下层的实际函数在获取异常时不会执行任何操作。所以它让它上升。
5)当异常到达模拟器和编译器之间的层时,编译器再次CATCHES_ALL,隐藏任何细节并抛出更具体的错误:“无法编译脚本XXX”
6)最后再重复一次循环,调用编译器的模拟器函数就可以了。
7)最后的边界是给用户的。用户是LAYER,全部适用。主要尝试catches_ALL,最后只是创建一个漂亮的对话框或页面,并向用户“抛出”翻译错误。
所以用户看到了。
模拟器:致命错误无法启动模拟器
- 编译器:无法编译脚本FOO1
- TextLoader:无法读取文件foo1.scp
--- trl:FileNotFound
比较:
a)编译器:NullPointer异常< -common案例和丢失的夜晚调试文件名错误
b)Loader:找不到文件< - 我是否提到加载程序加载了数百个脚本?
或
c)没有任何事情发生,因为所有人都被忽略了!!!
当然,这假设在每次重新抛出时你都不会忘记设置原因异常。
我的2cts。这个简单的规则多次挽救了我的生命......
-Ale
答案 3 :(得分:0)
你可以这样做:
try
{
var fileInfo = new FileInfo(path);
}
catch (System.IO.PathTooLongException ex)
{
handleError(ex);
}
catch (System.ArgumentException ex)
{
handleError(ex);
}
catch (System.UnauthorizedAccessException ex)
{
}
catch (System.SecurityException ex)
{
handleError(ex);
}
public void handleError(Exception ex)
{
//do some stuff
}