我的方法应该抛出自己的异常,还是如果文件不存在而让.NET抛出?


75

这是我的代码:

public void ReadSomeFile(string filePath)
{
    if (!File.Exists(filePath))
        throw new FileNotFoundException();

    var stream = new FileStream(filePath, ....)
    .....
}

我是否应该自己抛出异常(请参阅File.Exists检查)?如果文件不存在,FileStream将已经抛出FileNotFoundException。这里有什么好的编程习惯?代码分析表明,我们应该验证参数。但是,如果我将该参数直接传递给另一个方法(我的代码或其他代码),并且该方法本身会引发异常,那么在我的代码中验证参数的好处是什么?


25
毫无意义地抛出FileNotFoundException-实际上,这只是在引发种族状况问题。您可以处理异常,让其传播或将其包装在您自己的异常中。这分别对应于“我知道该怎么办”,“我不知道该怎么办”和“我想在堆栈上更高级地处理”。
a安2015年

旁注:如果您真的需要验证参数,则可以检查filePath看起来是否有效(即,绝对路径,或至少不包含Path.GetInvalidFileNameChars()
Alexei Levenkov 2015年

2
@AlexeiLevenkov我认为这也不是必需的。FileStream将处理此问题,
fhnaseer 2015年

3
相关:来自Eric Lippert的博客-Vexing例外-此案属于“外来例外”
科比2015年

这实际上取决于您要实现的目标。因此对此没有“正确答案”。这要取决于
瑞典2015​​年

Answers:


118

if (File.Exists(f)) { DoSomething(f) }(或其否定)是反模式。可以在这两个语句之间删除或创建该文件,因此以这种方式检查其存在意义不大。

除此之外,正如注释中指出的那样,尽管File.Exists()可能返回true,但是由于各种原因,实际打开文件仍然可能失败。因此,您将不得不重复错误检查并抛出文件的开头。

因为您不想重复自己,而是让代码保持DRY,只需尝试打开文件并new FileStream()抛出。然后,您可以捕获该异常,并根据需要重新引发原始异常或引发特定于应用程序的异常。

当然,调用File.Exists()是可以辩解的,但不能采用这种方式。


3
@CodeCaster当然。并且其他一些过程可以在File.Open(f);抛出和捕获之间创建文件。问题较少是情况在您处理之前可能会有所改变(因为在失败情况下这几乎是不可避免的),而是您应该对错误进行一次检查以避免重复(并且无论如何都必须对错误进行检查)。(还请注意,在与某些文件系统对话时,成功打开并不意味着不会删除您的文件:因此,即使打开似乎起作用,即使失败也可能发生。)
Yakk-Adam Nevraumont

2
除了竞争条件外,此代码还忽略了文件存在但无法读取(由于权限等原因)的情况,甚至可能还会出现更多错误。
el.pescado 2015年

8
虽然检查文件的存在并立即尝试打开它通常是一种反模式,但是如果其他逻辑将检查和打开分开,我不会认为它是反模式。在某些网络系统上,检查文件的明显可用性可能比获取打开文件所需的锁便宜。如果除非有多个文件可用,否则代码将无法做任何有用的事情,那么在获取打开任何文件所需的锁之前,确保所有文件似乎都可用可能很有用。
2015年

3
Python的策略名为“比权限更容易获得宽恕”(EAFP)。这意味着有时候,由于竞争条件和您无法控制的事情,最好只是让您的程序执行它想要做的事情,并在那里处理所有可能的错误(异常),而不是在每一步都检查条件是最佳的。在此示例中,我认为EAFP可以应用于最佳实践。
sleblanc

1
也可能发生FileNotFoundException,因为用户没有权限查看它或文件夹不存在...因此,如果您想帮助用户,则可以在捕获中检查更具体的原因,例如Folder.Exists -或只是确保您的友好用户消息说“找不到或访问文件”!
AndrewD

15

您的方法被调用ReadSomeFile并将其filename作为输入,因此抛出a是合理的FileNotFoundException。由于无法通过捕获异常然后自己抛出异常来添加任何值,因此请让.NET抛出该异常。

但是,如果您的方法过去LoadData(databaseName)并且必须访问许多文件,则捕获异常并引发自定义异常可能很有价值,因为您可以将databaseName连同其他有用信息一起添加到异常中。


即使在最后一种情况下,您也应尝试捕获内部异常,而不要使用File.Exists。
Stig Hemmer,2015年

在这种情况下,实际上可以回答问题的答案的投票少得多
Andriy Tylychko 2015年

1

除了已经给出的答案之外,您还可以说这取决于您期望发生的情况。

如果要读取日志文件并且该文件不存在,是否要引发错误,或者仅引发空字符串(或空字符串数组)?

如果返回默认值(例如空字符串),我只需将函数的内容包装在try-catch(但仅用于预期的错误)中,然后在catch块中返回默认值,同时在块中返回实际内容try

这将导致三种可能的情况:

  1. 返回文件的内容;
  2. 返回默认值,因为发生了预期的错误。
  3. .NET引发错误,因为您没有捕获到该特定错误。

0

当您不知道完整文件名(例如特殊文件名(例如,设备文件UNC路径))时,让正确的方法尝试打开文件:

在某些情况下,其他文件方法可能会失败,但是打开文件成功。

特殊文件名的一些示例是:

  • 骗子
  • NUL
  • COM1,COM2,COM3,COM4
  • \\服务器\共享\文件路径
  • \\ teela \ admin $ \ system32(到达C:\ WINNT \ system32)
  • C:.. \ File.txt
  • \\。\ COM1
  • %TEMP%
  • 和更多...

2
我对Windows不太了解,但是我可以肯定%TEMP%是环境变量,而不是文件,并且“打开文件”功能不会扩展它。
Functino
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.