我已经阅读了许多关于正确异常处理的博客/文章/书籍章节,但我仍然不清楚这个主题。我将尝试用以下示例来说明我的问题。
考虑具有以下要求的类方法:
因此规格很简单,以下是我开始编码的方法:
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的下一个规则是:只捕获你可以处理的特定异常。好吧,我不关心处理任何可以抛出的特定异常,我只是想检查文件是否可以读取。我怎样才能以恰当,最佳的方式做到这一点?
答案 0 :(得分:4)
您的要求很明确 - 跳过无法阅读的文件。那么一般异常处理程序有什么问题呢?它允许您以简单,清晰,可读,可扩展和可维护的方式执行任务。
如果您希望在将来某个日期以不同的方式处理多个可能的异常,您可以在常规异常之上添加特定异常的捕获。
所以你宁愿看下面的代码?请注意,如果您添加更多代码来处理文件读取,则必须向此列表添加任何新例外。这一切什么都不做?
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
答案 1 :(得分:4)
虽然通常不认为捕捉和吞下非特定例外是一种好习惯,但风险往往被夸大了。
毕竟,ASP.NET将捕获在处理请求期间抛出的非特定异常,并且在将其包装到HttpUnhandledException之后,将重定向到错误页面并继续愉快地继续它。
在您的情况下,如果您想要遵守指南,则需要提供可以抛出的所有异常的完整列表。我相信以下列表已经完成:
UnauthorizedAccessException
IOException
FileNotFoundException
DirectoryNotFoundException
PathTooLongException
NotSupportedException
(路径格式不正确)。
SecurityException
ArgumentException
您可能想要捕获SecurityException或ArgumentException,其他一些来自IOException
,因此您可能想要捕获IOException
,{{1} }和NotSupportedException
。
答案 2 :(得分:2)
我对此问题的解决方案通常基于可能的例外情况。如果只有少数,我为每个指定catch块。如果有很多可能,我会抓住所有例外情况。强迫开发人员始终捕获特定的异常可能会导致一些非常难看的代码。
答案 3 :(得分:2)
在这个例子中你可以考虑的事情是FileNotFoundException
,你无法捕捉,因为它们太多了,而且最常见的Exception
,还有层{ {1}}。
通常,您将尝试尽可能具体地捕获异常,但尤其是如果您捕获异常而不实际使用它们来抛出错误,您可能会捕获一组异常。即使这样,你也会尝试尽可能具体化
答案 4 :(得分:2)
您正在一种方法中混合不同的操作,更改代码会让您更容易问题:
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方法是否正确,或者是否需要错误处理。
答案 5 :(得分:1)
这重复了所说的内容,但希望在某种程度上让你更好地理解。
“跳过任何因某些原因无法读取内容的文件”中存在逻辑错误。
如果您的代码中存在错误原因,则不要跳过它 您只想跳过存在文件相关错误的文件 如果FileContent中的ctor抛出错误怎么办?
例外是昂贵的
我会测试FileExists(并且仍然会捕获异常)
我同意乔列出的例外情况
来吧MSDN上有明确的例子,说明如何捕捉各种异常
答案 6 :(得分:1)
在我看来,将例外分为三种类型。首先是您期望的异常并且知道如何恢复。其次是您知道可以在运行时避免的异常。第三种是你不希望在运行时发生的那些,但无法避免或无法实际处理。
处理第一种类型,这些是对您的特定抽象级别有效的异常类,它们代表在该级别恢复的有效业务案例(在您的情况下,忽略。)
应该避免第二类例外 - 不要懒惰。应该让第三类例外通过......您需要确保知道如何处理问题,否则您可能会使您的应用程序处于混乱或无效状态。
正如其他人所说,你可以通过向现有的try块添加更多的catch块来处理多个异常,它们按照它们出现的顺序进行评估,所以如果你必须处理从你也处理的其他异常派生的异常,首先使用更具体的一个。