问题 在C#中处理文件时如何正确处理异常
我已经阅读了许多关于正确异常处理的博客/文章/书籍章节,但我仍然不清楚这个主题。我将尝试用以下示例来说明我的问题。
考虑具有以下要求的类方法:
- 接收文件路径列表作为参数
- 读取每个文件的文件内容,如果尝试这样做有任何问题,请跳过
- 返回表示文件内容的对象列表
所以规格很简单,这就是我开始编码的方式:
public class FileContent
{
public string FilePath { get; set; }
public byte[] Content { get; set; }
public FileContent(string filePath, byte[] content)
{
this.FilePath = filePath;
this.Content = content;
}
}
static List<FileContent> GetFileContents(List<string> paths)
{
var resultList = new List<FileContent>();
foreach (var path in paths)
{
// open file pointed by "path"
// read file to FileContent object
// add FileContent to resultList
// close file
}
return resultList;
}
现在请注意,规范中的2.表示该方法应该“跳过因某些原因无法读取内容的文件”。因此,可能有许多不同的原因发生(例如,文件不存在,文件访问由于缺乏安全权限而被拒绝,文件被锁定并被其他应用程序使用等等)但重点是我应该不在乎原因是什么,我只想尽可能读取文件的内容,否则就跳过文件。我不在乎错误是什么......
那么如何正确实现这个方法呢?
好的,正确的异常处理的第一个规则永远不会捕获一般的异常。所以这段代码不好:
static List<FileContent> GetFileContents(List<string> paths)
{
var resultList = new List<FileContent>();
foreach (var path in paths)
{
try
{
using (FileStream stream = File.Open(path, FileMode.Open))
using (BinaryReader reader = new BinaryReader(stream))
{
int fileLength = (int)stream.Length;
byte[] buffer = new byte[fileLength];
reader.Read(buffer, 0, fileLength);
resultList.Add(new FileContent(path, buffer));
}
}
catch (Exception ex)
{
// this file can't be read, do nothing... just skip the file
}
}
return resultList;
}
适当的异常handlig的下一个规则是:只捕获你可以处理的特定异常。好吧,我不关心处理任何可以抛出的特定异常,我只想检查文件是否可以读取。我怎样才能以正确,最佳的方式做到这一点?
3652
2017-11-07 14:36
起源
答案:
尽管捕获和吞下非特定例外通常不被视为良好做法,但风险往往被夸大。
毕竟,ASP.NET将捕获在处理请求期间抛出的非特定异常,并且在将其包装在HttpUnhandledException之后,将重定向到错误页面并继续愉快地继续它。
在您的情况下,如果您想要遵守指南,则需要提供可以抛出的所有异常的完整列表。我相信以下列表是完整的:
UnauthorizedAccessException
IOException
FileNotFoundException
DirectoryNotFoundException
PathTooLongException
NotSupportedException
(路径的格式不正确)。
SecurityException
ArgumentException
您可能不希望捕获SecurityException或ArgumentException,而其他几个派生自 IOException
,所以你可能想抓住 IOException
, NotSupportedException
和 UnauthorizedAccessException
。
4
2017-11-07 15:41
您的要求很明确 - 跳过无法读取的文件。那么一般异常处理程序有什么问题呢?它允许您以简单,清晰,可读,可扩展和可维护的方式执行任务。
如果您希望在将来某个日期以不同的方式处理多个可能的异常,则可以在常规异常之上添加特定异常的catch。
所以你宁愿看下面的代码?请注意,如果您添加更多代码来处理文件读取,那么您 必须 在此列表中添加任何新的例外。这一切什么都不做?
try
{
// find, open, read files
}
catch(FileNotFoundException) { }
catch(AccessViolation) { }
catch(...) { }
catch(...) { }
catch(...) { }
catch(...) { }
catch(...) { }
catch(...) { }
约定是指导,并且很好地坚持创建良好的代码 - 但不要过度复杂代码只是为了保持一些奇怪的正确礼仪感。
对我而言,正确的礼仪是不要在浴室里说话 - 永远。但当老板在那里向你打招呼的时候,你又打招呼了。因此,如果您不小心处理多个异常,则无需捕获每个异常。
编辑:所以我推荐以下内容
try
{
// find, open, read files
}
catch { } // Ignore any and all exceptions
上面告诉我不关心抛出哪个异常。通过不指定异常,即使只是System.Exception,我也允许.NET默认为它。所以下面是完全相同的代码。
try
{
// find, open, read files
}
catch(Exception) { } // Ignore any and all exceptions
或者,如果您要至少记录它:
try
{
// find, open, read files
}
catch(Exception ex) { Logger.Log(ex); } // Log any and all exceptions
4
2017-11-07 14:58
我对这个问题的解决方案通常基于可能的例外情况。如果只有少数几个,我为每个指定catch块。如果有很多可能,我会抓住所有例外情况。强制开发人员总是捕获特定的异常会导致一些非常丑陋的代码。
2
2017-11-07 14:51
您正在一种方法中混合不同的操作,更改代码将使您更容易问题:
static List<FileContent> GetFileContents(List<string> paths)
{
var resultList = new List<FileContent>();
foreach (var path in paths)
{
if (CanReadFile(path){
resultList.Add(new FileContent(path, buffer));
}
return resultList;
}
static bool CanReadFile(string Path){
try{
using (FileStream stream = File.Open(path, FileMode.Open))
using (BinaryReader reader = new BinaryReader(stream))
{
int fileLength = (int)stream.Length;
byte[] buffer = new byte[fileLength];
reader.Read(buffer, 0, fileLength);
}
}catch(Exception){ //I do not care what when wrong, error when reading from file
return false;
}
return true;
}
这样,CanReadFile会隐藏您的支票实施。您唯一需要考虑的是CanReadFile方法是否正确,或者是否需要错误处理。
2
2017-11-07 15:06
在这种情况下你可以考虑的东西是 FileNotFoundException
,你无法捕捉,因为它们太多,而且最普遍 Exception
,还有一层 IOException
。
通常,您将尝试尽可能详细地捕获异常,但特别是如果您捕获异常而不实际使用它们来抛出错误,您可能会捕获一组异常。即使这样,你也会尝试尽可能具体
2
2017-11-07 14:59
在我看来,将例外分为三种类型。首先是您期望的异常并且知道如何恢复。其次是您知道在运行时可以避免的异常。第三个是你不期望在运行时发生的那些,但是无法避免或无法实际处理。
处理第一种类型,这些是对您的特定抽象级别有效的异常类,它们代表在该级别恢复的有效业务案例(在您的情况下,忽略。)
应该避免第二类例外 - 不要懒惰。应该允许第三类异常通过...您需要确保知道如何处理问题,否则您可能会使应用程序处于混乱或无效状态。
正如其他人所说,你可以通过向现有的try块添加更多的catch块来处理多个异常,它们按照它们出现的顺序进行评估,所以如果你必须处理从你也处理的其他异常派生的异常,那就使用更多具体的第一个。
1
2017-11-14 13:50
这重复了所说的内容,但希望在某种程度上让您更好地理解。
您在“跳过因某些原因无法读取内容的任何文件”中出现逻辑错误。
如果这个原因是您的代码中的错误,您不想跳过它。
您只想跳过与文件相关的错误的文件。
如果FileContent中的ctor抛出错误该怎么办?
例外是昂贵的。
我会测试FileExists(并仍然捕获异常)
我同意Joe列出的例外情况
来吧MSDN有明确的例子,说明如何捕获各种异常
1
2017-11-07 21:10